From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- build/clang-plugin/.clang-format | 1 + build/clang-plugin/Makefile.in | 42 + build/clang-plugin/clang-plugin.cpp | 2331 ++++++++++++++++++++ build/clang-plugin/moz.build | 23 + build/clang-plugin/tests/Makefile.in | 18 + build/clang-plugin/tests/NonParameterTestCases.h | 61 + .../tests/TestAssertWithAssignment.cpp | 68 + .../tests/TestBadImplicitConversionCtor.cpp | 50 + build/clang-plugin/tests/TestCustomHeap.cpp | 28 + .../tests/TestExplicitOperatorBool.cpp | 11 + build/clang-plugin/tests/TestGlobalClass.cpp | 52 + build/clang-plugin/tests/TestHeapClass.cpp | 64 + .../TestInheritTypeAnnotationsFromTemplateArgs.cpp | 46 + build/clang-plugin/tests/TestKungFuDeathGrip.cpp | 107 + .../clang-plugin/tests/TestMultipleAnnotations.cpp | 17 + build/clang-plugin/tests/TestMustOverride.cpp | 63 + build/clang-plugin/tests/TestMustUse.cpp | 201 ++ build/clang-plugin/tests/TestNANTestingExpr.cpp | 16 + build/clang-plugin/tests/TestNANTestingExprC.c | 17 + build/clang-plugin/tests/TestNeedsNoVTableType.cpp | 94 + .../tests/TestNoAddRefReleaseOnReturn.cpp | 110 + .../tests/TestNoArithmeticExprInArgument.cpp | 32 + build/clang-plugin/tests/TestNoAutoType.cpp | 41 + .../tests/TestNoDuplicateRefCntMember.cpp | 49 + .../tests/TestNoExplicitMoveConstructor.cpp | 25 + .../tests/TestNoRefcountedInsideLambdas.cpp | 651 ++++++ build/clang-plugin/tests/TestNonHeapClass.cpp | 62 + build/clang-plugin/tests/TestNonMemMovable.cpp | 830 +++++++ build/clang-plugin/tests/TestNonMemMovableStd.cpp | 21 + .../clang-plugin/tests/TestNonParameterChecker.cpp | 179 ++ build/clang-plugin/tests/TestNonTemporaryClass.cpp | 70 + build/clang-plugin/tests/TestOverrideBaseCall.cpp | 175 ++ .../tests/TestOverrideBaseCallAnnotation.cpp | 47 + .../tests/TestRefCountedCopyConstructor.cpp | 25 + build/clang-plugin/tests/TestSprintfLiteral.cpp | 47 + build/clang-plugin/tests/TestStackClass.cpp | 50 + build/clang-plugin/tests/TestTrivialCtorDtor.cpp | 83 + build/clang-plugin/tests/moz.build | 45 + 38 files changed, 5852 insertions(+) create mode 100644 build/clang-plugin/.clang-format create mode 100644 build/clang-plugin/Makefile.in create mode 100644 build/clang-plugin/clang-plugin.cpp create mode 100644 build/clang-plugin/moz.build create mode 100644 build/clang-plugin/tests/Makefile.in create mode 100644 build/clang-plugin/tests/NonParameterTestCases.h create mode 100644 build/clang-plugin/tests/TestAssertWithAssignment.cpp create mode 100644 build/clang-plugin/tests/TestBadImplicitConversionCtor.cpp create mode 100644 build/clang-plugin/tests/TestCustomHeap.cpp create mode 100644 build/clang-plugin/tests/TestExplicitOperatorBool.cpp create mode 100644 build/clang-plugin/tests/TestGlobalClass.cpp create mode 100644 build/clang-plugin/tests/TestHeapClass.cpp create mode 100644 build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp create mode 100644 build/clang-plugin/tests/TestKungFuDeathGrip.cpp create mode 100644 build/clang-plugin/tests/TestMultipleAnnotations.cpp create mode 100644 build/clang-plugin/tests/TestMustOverride.cpp create mode 100644 build/clang-plugin/tests/TestMustUse.cpp create mode 100644 build/clang-plugin/tests/TestNANTestingExpr.cpp create mode 100644 build/clang-plugin/tests/TestNANTestingExprC.c create mode 100644 build/clang-plugin/tests/TestNeedsNoVTableType.cpp create mode 100644 build/clang-plugin/tests/TestNoAddRefReleaseOnReturn.cpp create mode 100644 build/clang-plugin/tests/TestNoArithmeticExprInArgument.cpp create mode 100644 build/clang-plugin/tests/TestNoAutoType.cpp create mode 100644 build/clang-plugin/tests/TestNoDuplicateRefCntMember.cpp create mode 100644 build/clang-plugin/tests/TestNoExplicitMoveConstructor.cpp create mode 100644 build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp create mode 100644 build/clang-plugin/tests/TestNonHeapClass.cpp create mode 100644 build/clang-plugin/tests/TestNonMemMovable.cpp create mode 100644 build/clang-plugin/tests/TestNonMemMovableStd.cpp create mode 100644 build/clang-plugin/tests/TestNonParameterChecker.cpp create mode 100644 build/clang-plugin/tests/TestNonTemporaryClass.cpp create mode 100644 build/clang-plugin/tests/TestOverrideBaseCall.cpp create mode 100644 build/clang-plugin/tests/TestOverrideBaseCallAnnotation.cpp create mode 100644 build/clang-plugin/tests/TestRefCountedCopyConstructor.cpp create mode 100644 build/clang-plugin/tests/TestSprintfLiteral.cpp create mode 100644 build/clang-plugin/tests/TestStackClass.cpp create mode 100644 build/clang-plugin/tests/TestTrivialCtorDtor.cpp create mode 100644 build/clang-plugin/tests/moz.build (limited to 'build/clang-plugin') diff --git a/build/clang-plugin/.clang-format b/build/clang-plugin/.clang-format new file mode 100644 index 000000000..9b3aa8b72 --- /dev/null +++ b/build/clang-plugin/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/build/clang-plugin/Makefile.in b/build/clang-plugin/Makefile.in new file mode 100644 index 000000000..ff1e39d1b --- /dev/null +++ b/build/clang-plugin/Makefile.in @@ -0,0 +1,42 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# LLVM_CXXFLAGS comes with its own optimization flags. +MOZ_OPTIMIZE = + +include $(topsrcdir)/config/config.mk + +# In the current moz.build world, we need to override essentially every +# variable to limit ourselves to what we need to build the clang plugin. +ifeq ($(HOST_OS_ARCH),WINNT) +OS_CXXFLAGS := $(LLVM_CXXFLAGS) -GR- -EHsc +else +OS_CXXFLAGS := $(LLVM_CXXFLAGS) -fno-rtti -fno-exceptions +DSO_LDOPTS := -shared +endif +OS_COMPILE_CXXFLAGS := +OS_LDFLAGS := $(LLVM_LDFLAGS) $(CLANG_LDFLAGS) + +ifeq ($(HOST_OS_ARCH)_$(OS_ARCH),Linux_Darwin) +# Use the host compiler instead of the target compiler. +CXX := $(HOST_CXX) +# expandlibs doesn't know the distinction between host and target toolchains, +# and on cross linux/darwin builds, the options to give to the linker for file +# lists differ between both, so don't use file lists. +EXPAND_MKSHLIB_ARGS := +# Don't pass OSX linker arguments. +MOZ_FIX_LINK_PATHS := +endif + +# Use the default OS X deployment target to enable using the libc++ headers +# correctly. Note that the binary produced here is a host tool and doesn't need +# to be distributed. +MACOSX_DEPLOYMENT_TARGET := + +# Temporarily relax the requirements for libstdc++ symbol versions on static +# analysis plugin in order to use a recent clang by accepting libstdc++ from +# gcc 4.4.0 (GLIBCXX_3.4.11). +ifdef CHECK_STDCXX +CHECK_STDCXX = $(call CHECK_SYMBOLS,$(1),GLIBCXX,libstdc++,v[1] > 3 || (v[1] == 3 && v[2] == 4 && v[3] > 11)) +endif diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp new file mode 100644 index 000000000..f420d3e8b --- /dev/null +++ b/build/clang-plugin/clang-plugin.cpp @@ -0,0 +1,2331 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This file respects the LLVM coding standard described at + * http://llvm.org/docs/CodingStandards.html */ + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include +#include + +#define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR) + +using namespace llvm; +using namespace clang; + +#if CLANG_VERSION_FULL >= 306 +typedef std::unique_ptr ASTConsumerPtr; +#else +typedef ASTConsumer *ASTConsumerPtr; +#endif + +#ifndef HAVE_NEW_ASTMATCHER_NAMES +// In clang 3.8, a number of AST matchers were renamed to better match the +// respective AST node. We use the new names, and #define them to the old +// ones for compatibility with older versions. +#define cxxConstructExpr constructExpr +#define cxxConstructorDecl constructorDecl +#define cxxMethodDecl methodDecl +#define cxxNewExpr newExpr +#define cxxRecordDecl recordDecl +#endif + +#ifndef HAS_ACCEPTS_IGNORINGPARENIMPCASTS +#define hasIgnoringParenImpCasts(x) has(x) +#else +// Before clang 3.9 "has" would behave like has(ignoringParenImpCasts(x)), +// however doing that explicitly would not compile. +#define hasIgnoringParenImpCasts(x) has(ignoringParenImpCasts(x)) +#endif + +// Check if the given expression contains an assignment expression. +// This can either take the form of a Binary Operator or a +// Overloaded Operator Call. +bool hasSideEffectAssignment(const Expr *Expression) { + if (auto OpCallExpr = dyn_cast_or_null(Expression)) { + auto BinOp = OpCallExpr->getOperator(); + if (BinOp == OO_Equal || (BinOp >= OO_PlusEqual && BinOp <= OO_PipeEqual)) { + return true; + } + } else if (auto BinOpExpr = dyn_cast_or_null(Expression)) { + if (BinOpExpr->isAssignmentOp()) { + return true; + } + } + + // Recurse to children. + for (const Stmt *SubStmt : Expression->children()) { + auto ChildExpr = dyn_cast_or_null(SubStmt); + if (ChildExpr && hasSideEffectAssignment(ChildExpr)) { + return true; + } + } + + return false; +} + +namespace { + +using namespace clang::ast_matchers; +class DiagnosticsMatcher { +public: + DiagnosticsMatcher(); + + ASTConsumerPtr makeASTConsumer() { return AstMatcher.newASTConsumer(); } + +private: + class ScopeChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class ArithmeticArgChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class TrivialCtorDtorChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NaNExprChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NoAddRefReleaseOnReturnChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class RefCountedInsideLambdaChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + void emitDiagnostics(SourceLocation Loc, StringRef Name, QualType Type); + + private: + class ThisVisitor : public RecursiveASTVisitor { + public: + explicit ThisVisitor(RefCountedInsideLambdaChecker& Checker) + : Checker(Checker) {} + + bool VisitCXXThisExpr(CXXThisExpr *This); + private: + RefCountedInsideLambdaChecker& Checker; + }; + + ASTContext *Context; + }; + + class ExplicitOperatorBoolChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NoDuplicateRefCntMemberChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NeedsNoVTableTypeChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NonMemMovableTemplateArgChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NonMemMovableMemberChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class ExplicitImplicitChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NoAutoTypeChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NoExplicitMoveConstructorChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class RefCountedCopyConstructorChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class AssertAssignmentChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class KungFuDeathGripChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class SprintfLiteralChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class OverrideBaseCallChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + private: + void evaluateExpression(const Stmt *StmtExpr, + std::list &MethodList); + void getRequiredBaseMethod(const CXXMethodDecl* Method, + std::list& MethodsList); + void findBaseMethodCall(const CXXMethodDecl* Method, + std::list& MethodsList); + bool isRequiredBaseMethod(const CXXMethodDecl *Method); + }; + +/* + * This is a companion checker for OverrideBaseCallChecker that rejects + * the usage of MOZ_REQUIRED_BASE_METHOD on non-virtual base methods. + */ + class OverrideBaseCallUsageChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NonParamInsideFunctionDeclChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + ScopeChecker Scope; + ArithmeticArgChecker ArithmeticArg; + TrivialCtorDtorChecker TrivialCtorDtor; + NaNExprChecker NaNExpr; + NoAddRefReleaseOnReturnChecker NoAddRefReleaseOnReturn; + RefCountedInsideLambdaChecker RefCountedInsideLambda; + ExplicitOperatorBoolChecker ExplicitOperatorBool; + NoDuplicateRefCntMemberChecker NoDuplicateRefCntMember; + NeedsNoVTableTypeChecker NeedsNoVTableType; + NonMemMovableTemplateArgChecker NonMemMovableTemplateArg; + NonMemMovableMemberChecker NonMemMovableMember; + ExplicitImplicitChecker ExplicitImplicit; + NoAutoTypeChecker NoAutoType; + NoExplicitMoveConstructorChecker NoExplicitMoveConstructor; + RefCountedCopyConstructorChecker RefCountedCopyConstructor; + AssertAssignmentChecker AssertAttribution; + KungFuDeathGripChecker KungFuDeathGrip; + SprintfLiteralChecker SprintfLiteral; + OverrideBaseCallChecker OverrideBaseCall; + OverrideBaseCallUsageChecker OverrideBaseCallUsage; + NonParamInsideFunctionDeclChecker NonParamInsideFunctionDecl; + MatchFinder AstMatcher; +}; + +namespace { + +std::string getDeclarationNamespace(const Decl *Declaration) { + const DeclContext *DC = + Declaration->getDeclContext()->getEnclosingNamespaceContext(); + const NamespaceDecl *ND = dyn_cast(DC); + if (!ND) { + return ""; + } + + while (const DeclContext *ParentDC = ND->getParent()) { + if (!isa(ParentDC)) { + break; + } + ND = cast(ParentDC); + } + + const auto &Name = ND->getName(); + return Name; +} + +bool isInIgnoredNamespaceForImplicitCtor(const Decl *Declaration) { + std::string Name = getDeclarationNamespace(Declaration); + if (Name == "") { + return false; + } + + return Name == "std" || // standard C++ lib + Name == "__gnu_cxx" || // gnu C++ lib + Name == "boost" || // boost + Name == "webrtc" || // upstream webrtc + Name == "rtc" || // upstream webrtc 'base' package + Name.substr(0, 4) == "icu_" || // icu + Name == "google" || // protobuf + Name == "google_breakpad" || // breakpad + Name == "soundtouch" || // libsoundtouch + Name == "stagefright" || // libstagefright + Name == "MacFileUtilities" || // MacFileUtilities + Name == "dwarf2reader" || // dwarf2reader + Name == "arm_ex_to_module" || // arm_ex_to_module + Name == "testing" || // gtest + Name == "Json"; // jsoncpp +} + +bool isInIgnoredNamespaceForImplicitConversion(const Decl *Declaration) { + std::string Name = getDeclarationNamespace(Declaration); + if (Name == "") { + return false; + } + + return Name == "std" || // standard C++ lib + Name == "__gnu_cxx" || // gnu C++ lib + Name == "google_breakpad" || // breakpad + Name == "testing"; // gtest +} + +bool isIgnoredPathForImplicitCtor(const Decl *Declaration) { + SourceLocation Loc = Declaration->getLocation(); + const SourceManager &SM = Declaration->getASTContext().getSourceManager(); + SmallString<1024> FileName = SM.getFilename(Loc); + llvm::sys::fs::make_absolute(FileName); + llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName), + End = llvm::sys::path::rend(FileName); + for (; Begin != End; ++Begin) { + if (Begin->compare_lower(StringRef("skia")) == 0 || + Begin->compare_lower(StringRef("angle")) == 0 || + Begin->compare_lower(StringRef("harfbuzz")) == 0 || + Begin->compare_lower(StringRef("hunspell")) == 0 || + Begin->compare_lower(StringRef("scoped_ptr.h")) == 0 || + Begin->compare_lower(StringRef("graphite2")) == 0 || + Begin->compare_lower(StringRef("icu")) == 0) { + return true; + } + if (Begin->compare_lower(StringRef("chromium")) == 0) { + // Ignore security/sandbox/chromium but not ipc/chromium. + ++Begin; + return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0; + } + } + return false; +} + +bool isIgnoredPathForImplicitConversion(const Decl *Declaration) { + Declaration = Declaration->getCanonicalDecl(); + SourceLocation Loc = Declaration->getLocation(); + const SourceManager &SM = Declaration->getASTContext().getSourceManager(); + SmallString<1024> FileName = SM.getFilename(Loc); + llvm::sys::fs::make_absolute(FileName); + llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName), + End = llvm::sys::path::rend(FileName); + for (; Begin != End; ++Begin) { + if (Begin->compare_lower(StringRef("graphite2")) == 0) { + return true; + } + if (Begin->compare_lower(StringRef("chromium")) == 0) { + // Ignore security/sandbox/chromium but not ipc/chromium. + ++Begin; + return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0; + } + } + return false; +} + +bool isIgnoredPathForSprintfLiteral(const CallExpr *Call, const SourceManager &SM) { + SourceLocation Loc = Call->getLocStart(); + SmallString<1024> FileName = SM.getFilename(Loc); + llvm::sys::fs::make_absolute(FileName); + llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName), + End = llvm::sys::path::rend(FileName); + for (; Begin != End; ++Begin) { + if (Begin->compare_lower(StringRef("angle")) == 0 || + Begin->compare_lower(StringRef("chromium")) == 0 || + Begin->compare_lower(StringRef("crashreporter")) == 0 || + Begin->compare_lower(StringRef("google-breakpad")) == 0 || + Begin->compare_lower(StringRef("harfbuzz")) == 0 || + Begin->compare_lower(StringRef("libstagefright")) == 0 || + Begin->compare_lower(StringRef("mtransport")) == 0 || + Begin->compare_lower(StringRef("protobuf")) == 0 || + Begin->compare_lower(StringRef("skia")) == 0 || + // Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof + Begin->compare_lower(StringRef("testing")) == 0) { + return true; + } + if (Begin->compare_lower(StringRef("webrtc")) == 0) { + // Ignore trunk/webrtc, but not media/webrtc + ++Begin; + return Begin != End && Begin->compare_lower(StringRef("trunk")) == 0; + } + } + return false; +} + +bool isInterestingDeclForImplicitConversion(const Decl *Declaration) { + return !isInIgnoredNamespaceForImplicitConversion(Declaration) && + !isIgnoredPathForImplicitConversion(Declaration); +} + +bool isIgnoredExprForMustUse(const Expr *E) { + if (const CXXOperatorCallExpr *OpCall = dyn_cast(E)) { + switch (OpCall->getOperator()) { + case OO_Equal: + case OO_PlusEqual: + case OO_MinusEqual: + case OO_StarEqual: + case OO_SlashEqual: + case OO_PercentEqual: + case OO_CaretEqual: + case OO_AmpEqual: + case OO_PipeEqual: + case OO_LessLessEqual: + case OO_GreaterGreaterEqual: + return true; + default: + return false; + } + } + + if (const BinaryOperator *Op = dyn_cast(E)) { + return Op->isAssignmentOp(); + } + + return false; +} + +template +StringRef getNameChecked(const T& D) { + return D->getIdentifier() ? D->getName() : ""; +} + +bool typeIsRefPtr(QualType Q) { + CXXRecordDecl *D = Q->getAsCXXRecordDecl(); + if (!D || !D->getIdentifier()) { + return false; + } + + StringRef name = D->getName(); + if (name == "RefPtr" || name == "nsCOMPtr") { + return true; + } + return false; +} + +// The method defined in clang for ignoring implicit nodes doesn't work with +// some AST trees. To get around this, we define our own implementation of +// IgnoreImplicit. +const Stmt *IgnoreImplicit(const Stmt *s) { + while (true) { + if (auto *ewc = dyn_cast(s)) { + s = ewc->getSubExpr(); + } else if (auto *mte = dyn_cast(s)) { + s = mte->GetTemporaryExpr(); + } else if (auto *bte = dyn_cast(s)) { + s = bte->getSubExpr(); + } else if (auto *ice = dyn_cast(s)) { + s = ice->getSubExpr(); + } else { + break; + } + } + + return s; +} + +const Expr *IgnoreImplicit(const Expr *e) { + return cast(IgnoreImplicit(static_cast(e))); +} +} + +class CustomTypeAnnotation { + enum ReasonKind { + RK_None, + RK_Direct, + RK_ArrayElement, + RK_BaseClass, + RK_Field, + RK_TemplateInherited, + }; + struct AnnotationReason { + QualType Type; + ReasonKind Kind; + const FieldDecl *Field; + + bool valid() const { return Kind != RK_None; } + }; + typedef DenseMap ReasonCache; + + const char *Spelling; + const char *Pretty; + ReasonCache Cache; + +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(); + } + void dumpAnnotationReason(DiagnosticsEngine &Diag, QualType T, + SourceLocation Loc); + + void reportErrorIfPresent(DiagnosticsEngine &Diag, QualType T, + SourceLocation Loc, unsigned ErrorID, + unsigned NoteID) { + if (hasEffectiveAnnotation(T)) { + Diag.Report(Loc, ErrorID) << T; + Diag.Report(Loc, NoteID); + dumpAnnotationReason(Diag, T, Loc); + } + } + +private: + bool hasLiteralAnnotation(QualType T) const; + AnnotationReason directAnnotationReason(QualType T); + AnnotationReason tmplArgAnnotationReason(ArrayRef Args); + +protected: + // Allow subclasses to apply annotations to external code: + virtual bool hasFakeAnnotation(const TagDecl *D) const { return false; } +}; + +static CustomTypeAnnotation StackClass = + CustomTypeAnnotation("moz_stack_class", "stack"); +static CustomTypeAnnotation GlobalClass = + CustomTypeAnnotation("moz_global_class", "global"); +static CustomTypeAnnotation NonHeapClass = + CustomTypeAnnotation("moz_nonheap_class", "non-heap"); +static CustomTypeAnnotation HeapClass = + CustomTypeAnnotation("moz_heap_class", "heap"); +static CustomTypeAnnotation NonTemporaryClass = + CustomTypeAnnotation("moz_non_temporary_class", "non-temporary"); +static CustomTypeAnnotation MustUse = + CustomTypeAnnotation("moz_must_use_type", "must-use"); +static CustomTypeAnnotation NonParam = + CustomTypeAnnotation("moz_non_param", "non-param"); + +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 = getNameChecked(D); + 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; + const CompilerInstance &CI; + DiagnosticsMatcher Matcher; + +public: + MozChecker(const CompilerInstance &CI) : Diag(CI.getDiagnostics()), CI(CI) {} + + ASTConsumerPtr getOtherConsumer() { return Matcher.makeASTConsumer(); } + + virtual void HandleTranslationUnit(ASTContext &Ctx) override { + TraverseDecl(Ctx.getTranslationUnitDecl()); + } + + static bool hasCustomAnnotation(const Decl *D, const char *Spelling) { + iterator_range> Attrs = + D->specific_attrs(); + + for (AnnotateAttr *Attr : Attrs) { + if (Attr->getAnnotation() == Spelling) { + return true; + } + } + + return false; + } + + void handleUnusedExprResult(const Stmt *Statement) { + const Expr *E = dyn_cast_or_null(Statement); + if (E) { + E = E->IgnoreImplicit(); // Ignore ExprWithCleanup etc. implicit wrappers + QualType T = E->getType(); + if (MustUse.hasEffectiveAnnotation(T) && !isIgnoredExprForMustUse(E)) { + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Unused value of must-use type %0"); + + Diag.Report(E->getLocStart(), ErrorID) << T; + MustUse.dumpAnnotationReason(Diag, T, E->getLocStart()); + } + } + } + + bool VisitCXXRecordDecl(CXXRecordDecl *D) { + // We need definitions, not declarations + if (!D->isThisDeclarationADefinition()) + return true; + + // Look through all of our immediate bases to find methods that need to be + // overridden + typedef std::vector OverridesVector; + OverridesVector MustOverrides; + for (CXXRecordDecl::base_class_iterator Base = D->bases_begin(), + E = D->bases_end(); + Base != E; ++Base) { + // The base is either a class (CXXRecordDecl) or it's a templated class... + CXXRecordDecl *Parent = Base->getType() + .getDesugaredType(D->getASTContext()) + ->getAsCXXRecordDecl(); + // The parent might not be resolved to a type yet. In this case, we can't + // do any checking here. For complete correctness, we should visit + // template instantiations, but this case is likely to be rare, so we will + // ignore it until it becomes important. + if (!Parent) { + continue; + } + Parent = Parent->getDefinition(); + for (CXXRecordDecl::method_iterator M = Parent->method_begin(); + M != Parent->method_end(); ++M) { + if (hasCustomAnnotation(*M, "moz_must_override")) + MustOverrides.push_back(*M); + } + } + + for (OverridesVector::iterator It = MustOverrides.begin(); + It != MustOverrides.end(); ++It) { + bool Overridden = false; + for (CXXRecordDecl::method_iterator M = D->method_begin(); + !Overridden && M != D->method_end(); ++M) { + // The way that Clang checks if a method M overrides its parent method + // is if the method has the same name but would not overload. + if (getNameChecked(M) == getNameChecked(*It) && + !CI.getSema().IsOverload(*M, (*It), false)) { + Overridden = true; + break; + } + } + if (!Overridden) { + unsigned OverrideID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "%0 must override %1"); + unsigned OverrideNote = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "function to override is here"); + Diag.Report(D->getLocation(), OverrideID) << D->getDeclName() + << (*It)->getDeclName(); + Diag.Report((*It)->getLocation(), OverrideNote); + } + } + + return true; + } + + bool VisitSwitchCase(SwitchCase *Statement) { + handleUnusedExprResult(Statement->getSubStmt()); + return true; + } + bool VisitCompoundStmt(CompoundStmt *Statement) { + for (CompoundStmt::body_iterator It = Statement->body_begin(), + E = Statement->body_end(); + It != E; ++It) { + handleUnusedExprResult(*It); + } + return true; + } + bool VisitIfStmt(IfStmt *Statement) { + handleUnusedExprResult(Statement->getThen()); + handleUnusedExprResult(Statement->getElse()); + return true; + } + bool VisitWhileStmt(WhileStmt *Statement) { + handleUnusedExprResult(Statement->getBody()); + return true; + } + bool VisitDoStmt(DoStmt *Statement) { + handleUnusedExprResult(Statement->getBody()); + return true; + } + bool VisitForStmt(ForStmt *Statement) { + handleUnusedExprResult(Statement->getBody()); + handleUnusedExprResult(Statement->getInit()); + handleUnusedExprResult(Statement->getInc()); + return true; + } + bool VisitBinComma(BinaryOperator *Op) { + handleUnusedExprResult(Op->getLHS()); + return true; + } +}; + +/// A cached data of whether classes are refcounted or not. +typedef DenseMap> + RefCountedMap; +RefCountedMap RefCountedClasses; + +bool classHasAddRefRelease(const CXXRecordDecl *D) { + const RefCountedMap::iterator &It = RefCountedClasses.find(D); + if (It != RefCountedClasses.end()) { + return It->second.second; + } + + bool SeenAddRef = false; + bool SeenRelease = false; + for (CXXRecordDecl::method_iterator Method = D->method_begin(); + Method != D->method_end(); ++Method) { + const auto &Name = getNameChecked(Method); + if (Name == "AddRef") { + SeenAddRef = true; + } else if (Name == "Release") { + SeenRelease = true; + } + } + RefCountedClasses[D] = std::make_pair(D, SeenAddRef && SeenRelease); + return SeenAddRef && SeenRelease; +} + +bool isClassRefCounted(QualType T); + +bool isClassRefCounted(const CXXRecordDecl *D) { + // Normalize so that D points to the definition if it exists. + if (!D->hasDefinition()) + return false; + D = D->getDefinition(); + // Base class: anyone with AddRef/Release is obviously a refcounted class. + if (classHasAddRefRelease(D)) + return true; + + // Look through all base cases to figure out if the parent is a refcounted + // class. + for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(); + Base != D->bases_end(); ++Base) { + bool Super = isClassRefCounted(Base->getType()); + if (Super) { + return true; + } + } + + return false; +} + +bool isClassRefCounted(QualType T) { + while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) + T = ArrTy->getElementType(); + CXXRecordDecl *Clazz = T->getAsCXXRecordDecl(); + return Clazz ? isClassRefCounted(Clazz) : false; +} + +template bool ASTIsInSystemHeader(const ASTContext &AC, const T &D) { + auto &SourceManager = AC.getSourceManager(); + auto ExpansionLoc = SourceManager.getExpansionLoc(D.getLocStart()); + if (ExpansionLoc.isInvalid()) { + return false; + } + return SourceManager.isInSystemHeader(ExpansionLoc); +} + +const FieldDecl *getClassRefCntMember(const CXXRecordDecl *D) { + for (RecordDecl::field_iterator Field = D->field_begin(), E = D->field_end(); + Field != E; ++Field) { + if (getNameChecked(Field) == "mRefCnt") { + return *Field; + } + } + return 0; +} + +const FieldDecl *getBaseRefCntMember(QualType T); + +const FieldDecl *getBaseRefCntMember(const CXXRecordDecl *D) { + const FieldDecl *RefCntMember = getClassRefCntMember(D); + if (RefCntMember && isClassRefCounted(D)) { + return RefCntMember; + } + + for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(), + E = D->bases_end(); + Base != E; ++Base) { + RefCntMember = getBaseRefCntMember(Base->getType()); + if (RefCntMember) { + return RefCntMember; + } + } + return 0; +} + +const FieldDecl *getBaseRefCntMember(QualType T) { + while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) + T = ArrTy->getElementType(); + CXXRecordDecl *Clazz = T->getAsCXXRecordDecl(); + return Clazz ? getBaseRefCntMember(Clazz) : 0; +} + +bool typeHasVTable(QualType T) { + while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) + T = ArrTy->getElementType(); + CXXRecordDecl *Offender = T->getAsCXXRecordDecl(); + return Offender && Offender->hasDefinition() && Offender->isDynamicClass(); +} +} + +namespace clang { +namespace ast_matchers { + +/// This matcher will match any function declaration that is declared as a heap +/// allocator. +AST_MATCHER(FunctionDecl, heapAllocator) { + return MozChecker::hasCustomAnnotation(&Node, "moz_heap_allocator"); +} + +/// This matcher will match any declaration that is marked as not accepting +/// arithmetic expressions in its arguments. +AST_MATCHER(Decl, noArithmeticExprInArgs) { + return MozChecker::hasCustomAnnotation(&Node, "moz_no_arith_expr_in_arg"); +} + +/// This matcher will match any C++ class that is marked as having a trivial +/// constructor and destructor. +AST_MATCHER(CXXRecordDecl, hasTrivialCtorDtor) { + return MozChecker::hasCustomAnnotation(&Node, "moz_trivial_ctor_dtor"); +} + +/// This matcher will match any function declaration that is marked to prohibit +/// calling AddRef or Release on its return value. +AST_MATCHER(FunctionDecl, hasNoAddRefReleaseOnReturnAttr) { + return MozChecker::hasCustomAnnotation(&Node, + "moz_no_addref_release_on_return"); +} + +/// This matcher will match all arithmetic binary operators. +AST_MATCHER(BinaryOperator, binaryArithmeticOperator) { + BinaryOperatorKind OpCode = Node.getOpcode(); + return OpCode == BO_Mul || OpCode == BO_Div || OpCode == BO_Rem || + OpCode == BO_Add || OpCode == BO_Sub || OpCode == BO_Shl || + OpCode == BO_Shr || OpCode == BO_And || OpCode == BO_Xor || + OpCode == BO_Or || OpCode == BO_MulAssign || OpCode == BO_DivAssign || + OpCode == BO_RemAssign || OpCode == BO_AddAssign || + OpCode == BO_SubAssign || OpCode == BO_ShlAssign || + OpCode == BO_ShrAssign || OpCode == BO_AndAssign || + OpCode == BO_XorAssign || OpCode == BO_OrAssign; +} + +/// This matcher will match all arithmetic unary operators. +AST_MATCHER(UnaryOperator, unaryArithmeticOperator) { + UnaryOperatorKind OpCode = Node.getOpcode(); + return OpCode == UO_PostInc || OpCode == UO_PostDec || OpCode == UO_PreInc || + OpCode == UO_PreDec || OpCode == UO_Plus || OpCode == UO_Minus || + OpCode == UO_Not; +} + +/// This matcher will match == and != binary operators. +AST_MATCHER(BinaryOperator, binaryEqualityOperator) { + BinaryOperatorKind OpCode = Node.getOpcode(); + return OpCode == BO_EQ || OpCode == BO_NE; +} + +/// This matcher will match floating point types. +AST_MATCHER(QualType, isFloat) { return Node->isRealFloatingType(); } + +/// This matcher will match locations in system headers. This is adopted from +/// isExpansionInSystemHeader in newer clangs, but modified in order to work +/// with old clangs that we use on infra. +AST_MATCHER(BinaryOperator, isInSystemHeader) { + return ASTIsInSystemHeader(Finder->getASTContext(), Node); +} + +/// This matcher will match a list of files. These files contain +/// known NaN-testing expressions which we would like to whitelist. +AST_MATCHER(BinaryOperator, isInWhitelistForNaNExpr) { + const char* whitelist[] = { + "SkScalar.h", + "json_writer.cpp" + }; + + SourceLocation Loc = Node.getOperatorLoc(); + auto &SourceManager = Finder->getASTContext().getSourceManager(); + SmallString<1024> FileName = SourceManager.getFilename(Loc); + + for (auto itr = std::begin(whitelist); itr != std::end(whitelist); itr++) { + if (llvm::sys::path::rbegin(FileName)->equals(*itr)) { + return true; + } + } + + return false; +} + +/// This matcher will match all accesses to AddRef or Release methods. +AST_MATCHER(MemberExpr, isAddRefOrRelease) { + ValueDecl *Member = Node.getMemberDecl(); + CXXMethodDecl *Method = dyn_cast(Member); + if (Method) { + const auto &Name = getNameChecked(Method); + return Name == "AddRef" || Name == "Release"; + } + return false; +} + +/// This matcher will select classes which are refcounted. +AST_MATCHER(CXXRecordDecl, hasRefCntMember) { + return isClassRefCounted(&Node) && getClassRefCntMember(&Node); +} + +AST_MATCHER(QualType, hasVTable) { return typeHasVTable(Node); } + +AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) { + return MozChecker::hasCustomAnnotation(&Node, "moz_needs_no_vtable_type"); +} + +/// This matcher will select classes which are non-memmovable +AST_MATCHER(QualType, isNonMemMovable) { + return NonMemMovable.hasEffectiveAnnotation(Node); +} + +/// This matcher will select classes which require a memmovable template arg +AST_MATCHER(CXXRecordDecl, needsMemMovableTemplateArg) { + return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_type"); +} + +/// This matcher will select classes which require all members to be memmovable +AST_MATCHER(CXXRecordDecl, needsMemMovableMembers) { + return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_members"); +} + +AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) { + const CXXConstructorDecl *Declaration = Node.getCanonicalDecl(); + return + // Skip ignored namespaces and paths + !isInIgnoredNamespaceForImplicitCtor(Declaration) && + !isIgnoredPathForImplicitCtor(Declaration) && + // We only want Converting constructors + Declaration->isConvertingConstructor(false) && + // We don't want copy of move constructors, as those are allowed to be + // implicit + !Declaration->isCopyOrMoveConstructor() && + // We don't want deleted constructors. + !Declaration->isDeleted(); +} + +// We can't call this "isImplicit" since it clashes with an existing matcher in +// clang. +AST_MATCHER(CXXConstructorDecl, isMarkedImplicit) { + return MozChecker::hasCustomAnnotation(&Node, "moz_implicit"); +} + +AST_MATCHER(CXXRecordDecl, isConcreteClass) { return !Node.isAbstract(); } + +AST_MATCHER(QualType, autoNonAutoableType) { + if (const AutoType *T = Node->getContainedAutoType()) { + if (const CXXRecordDecl *Rec = T->getAsCXXRecordDecl()) { + return MozChecker::hasCustomAnnotation(Rec, "moz_non_autoable"); + } + } + return false; +} + +AST_MATCHER(CXXConstructorDecl, isExplicitMoveConstructor) { + return Node.isExplicit() && Node.isMoveConstructor(); +} + +AST_MATCHER(CXXConstructorDecl, isCompilerProvidedCopyConstructor) { + return !Node.isUserProvided() && Node.isCopyConstructor(); +} + +AST_MATCHER(CallExpr, isAssertAssignmentTestFunc) { + static const std::string AssertName = "MOZ_AssertAssignmentTest"; + const FunctionDecl *Method = Node.getDirectCallee(); + + return Method + && Method->getDeclName().isIdentifier() + && Method->getName() == AssertName; +} + +AST_MATCHER(CallExpr, isSnprintfLikeFunc) { + static const std::string Snprintf = "snprintf"; + static const std::string Vsnprintf = "vsnprintf"; + const FunctionDecl *Func = Node.getDirectCallee(); + + if (!Func || isa(Func)) { + return false; + } + + StringRef Name = getNameChecked(Func); + if (Name != Snprintf && Name != Vsnprintf) { + return false; + } + + return !isIgnoredPathForSprintfLiteral(&Node, Finder->getASTContext().getSourceManager()); +} + +AST_MATCHER(CXXRecordDecl, isLambdaDecl) { + return Node.isLambda(); +} + +AST_MATCHER(QualType, isRefPtr) { + return typeIsRefPtr(Node); +} + +AST_MATCHER(CXXRecordDecl, hasBaseClasses) { + const CXXRecordDecl *Decl = Node.getCanonicalDecl(); + + // Must have definition and should inherit other classes + return Decl && Decl->hasDefinition() && Decl->getNumBases(); +} + +AST_MATCHER(CXXMethodDecl, isRequiredBaseMethod) { + const CXXMethodDecl *Decl = Node.getCanonicalDecl(); + return Decl + && MozChecker::hasCustomAnnotation(Decl, "moz_required_base_method"); +} + +AST_MATCHER(CXXMethodDecl, isNonVirtual) { + const CXXMethodDecl *Decl = Node.getCanonicalDecl(); + return Decl && !Decl->isVirtual(); +} +} +} + +namespace { + +void CustomTypeAnnotation::dumpAnnotationReason(DiagnosticsEngine &Diag, + QualType T, + SourceLocation Loc) { + unsigned InheritsID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "%1 is a %0 type because it inherits from a %0 type %2"); + unsigned MemberID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "%1 is a %0 type because member %2 is a %0 type %3"); + unsigned ArrayID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "%1 is a %0 type because it is an array of %0 type %2"); + unsigned TemplID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "%1 is a %0 type because it has a template argument %0 type %2"); + + AnnotationReason Reason = directAnnotationReason(T); + for (;;) { + switch (Reason.Kind) { + case RK_ArrayElement: + Diag.Report(Loc, ArrayID) << Pretty << T << Reason.Type; + break; + case RK_BaseClass: { + const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl(); + assert(Declaration && "This type should be a C++ class"); + + Diag.Report(Declaration->getLocation(), InheritsID) << Pretty << T + << Reason.Type; + break; + } + case RK_Field: + Diag.Report(Reason.Field->getLocation(), MemberID) + << Pretty << T << Reason.Field << Reason.Type; + break; + case RK_TemplateInherited: { + const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl(); + assert(Declaration && "This type should be a C++ class"); + + Diag.Report(Declaration->getLocation(), TemplID) << Pretty << T + << Reason.Type; + break; + } + default: + // FIXME (bug 1203263): note the original annotation. + return; + } + + T = Reason.Type; + Reason = directAnnotationReason(T); + } +} + +bool CustomTypeAnnotation::hasLiteralAnnotation(QualType T) const { +#if CLANG_VERSION_FULL >= 306 + if (const TagDecl *D = T->getAsTagDecl()) { +#else + if (const CXXRecordDecl *D = T->getAsCXXRecordDecl()) { +#endif + return hasFakeAnnotation(D) || MozChecker::hasCustomAnnotation(D, Spelling); + } + return false; +} + +CustomTypeAnnotation::AnnotationReason +CustomTypeAnnotation::directAnnotationReason(QualType T) { + if (hasLiteralAnnotation(T)) { + AnnotationReason Reason = {T, RK_Direct, nullptr}; + return Reason; + } + + // Check if we have a cached answer + void *Key = T.getAsOpaquePtr(); + ReasonCache::iterator Cached = Cache.find(T.getAsOpaquePtr()); + if (Cached != Cache.end()) { + return Cached->second; + } + + // Check if we have a type which we can recurse into + if (const clang::ArrayType *Array = T->getAsArrayTypeUnsafe()) { + if (hasEffectiveAnnotation(Array->getElementType())) { + AnnotationReason Reason = {Array->getElementType(), RK_ArrayElement, + nullptr}; + Cache[Key] = Reason; + return Reason; + } + } + + // Recurse into Base classes + if (const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl()) { + if (Declaration->hasDefinition()) { + Declaration = Declaration->getDefinition(); + + for (const CXXBaseSpecifier &Base : Declaration->bases()) { + if (hasEffectiveAnnotation(Base.getType())) { + AnnotationReason Reason = {Base.getType(), RK_BaseClass, nullptr}; + Cache[Key] = Reason; + return Reason; + } + } + + // Recurse into members + for (const FieldDecl *Field : Declaration->fields()) { + if (hasEffectiveAnnotation(Field->getType())) { + AnnotationReason Reason = {Field->getType(), RK_Field, Field}; + Cache[Key] = Reason; + return Reason; + } + } + + // Recurse into template arguments if the annotation + // MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS is present + if (MozChecker::hasCustomAnnotation( + Declaration, "moz_inherit_type_annotations_from_template_args")) { + const ClassTemplateSpecializationDecl *Spec = + dyn_cast(Declaration); + if (Spec) { + const TemplateArgumentList &Args = Spec->getTemplateArgs(); + + AnnotationReason Reason = tmplArgAnnotationReason(Args.asArray()); + if (Reason.Kind != RK_None) { + Cache[Key] = Reason; + return Reason; + } + } + } + } + } + + AnnotationReason Reason = {QualType(), RK_None, nullptr}; + Cache[Key] = Reason; + return Reason; +} + +CustomTypeAnnotation::AnnotationReason +CustomTypeAnnotation::tmplArgAnnotationReason(ArrayRef Args) { + for (const TemplateArgument &Arg : Args) { + if (Arg.getKind() == TemplateArgument::Type) { + QualType Type = Arg.getAsType(); + if (hasEffectiveAnnotation(Type)) { + AnnotationReason Reason = {Type, RK_TemplateInherited, nullptr}; + return Reason; + } + } else if (Arg.getKind() == TemplateArgument::Pack) { + AnnotationReason Reason = tmplArgAnnotationReason(Arg.getPackAsArray()); + if (Reason.Kind != RK_None) { + return Reason; + } + } + } + + AnnotationReason Reason = {QualType(), RK_None, nullptr}; + return Reason; +} + +bool isPlacementNew(const CXXNewExpr *Expression) { + // Regular new expressions aren't placement new + if (Expression->getNumPlacementArgs() == 0) + return false; + const FunctionDecl *Declaration = Expression->getOperatorNew(); + if (Declaration && MozChecker::hasCustomAnnotation(Declaration, + "moz_heap_allocator")) { + return false; + } + return true; +} + +DiagnosticsMatcher::DiagnosticsMatcher() { + AstMatcher.addMatcher(varDecl().bind("node"), &Scope); + AstMatcher.addMatcher(cxxNewExpr().bind("node"), &Scope); + AstMatcher.addMatcher(materializeTemporaryExpr().bind("node"), &Scope); + AstMatcher.addMatcher( + callExpr(callee(functionDecl(heapAllocator()))).bind("node"), + &Scope); + AstMatcher.addMatcher(parmVarDecl().bind("parm_vardecl"), &Scope); + + AstMatcher.addMatcher( + callExpr(allOf(hasDeclaration(noArithmeticExprInArgs()), + anyOf(hasDescendant( + binaryOperator( + allOf(binaryArithmeticOperator(), + hasLHS(hasDescendant(declRefExpr())), + hasRHS(hasDescendant(declRefExpr())))) + .bind("node")), + hasDescendant( + unaryOperator( + allOf(unaryArithmeticOperator(), + hasUnaryOperand(allOf( + hasType(builtinType()), + anyOf(hasDescendant(declRefExpr()), + declRefExpr()))))) + .bind("node"))))) + .bind("call"), + &ArithmeticArg); + AstMatcher.addMatcher( + cxxConstructExpr( + allOf(hasDeclaration(noArithmeticExprInArgs()), + anyOf(hasDescendant( + binaryOperator( + allOf(binaryArithmeticOperator(), + hasLHS(hasDescendant(declRefExpr())), + hasRHS(hasDescendant(declRefExpr())))) + .bind("node")), + hasDescendant( + unaryOperator( + allOf(unaryArithmeticOperator(), + hasUnaryOperand(allOf( + hasType(builtinType()), + anyOf(hasDescendant(declRefExpr()), + declRefExpr()))))) + .bind("node"))))) + .bind("call"), + &ArithmeticArg); + + AstMatcher.addMatcher(cxxRecordDecl(hasTrivialCtorDtor()).bind("node"), + &TrivialCtorDtor); + + AstMatcher.addMatcher( + binaryOperator( + allOf(binaryEqualityOperator(), + hasLHS(hasIgnoringParenImpCasts( + declRefExpr(hasType(qualType((isFloat())))).bind("lhs"))), + hasRHS(hasIgnoringParenImpCasts( + declRefExpr(hasType(qualType((isFloat())))).bind("rhs"))), + unless(anyOf(isInSystemHeader(), isInWhitelistForNaNExpr())))) + .bind("node"), + &NaNExpr); + + // First, look for direct parents of the MemberExpr. + AstMatcher.addMatcher( + callExpr( + callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")), + hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr())) + .bind("member"))) + .bind("node"), + &NoAddRefReleaseOnReturn); + // Then, look for MemberExpr that need to be casted to the right type using + // an intermediary CastExpr before we get to the CallExpr. + AstMatcher.addMatcher( + callExpr( + callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")), + hasParent(castExpr( + hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr())) + .bind("member"))))) + .bind("node"), + &NoAddRefReleaseOnReturn); + + // We want to reject any code which captures a pointer to an object of a + // refcounted type, and then lets that value escape. As a primitive analysis, + // we reject any occurances of the lambda as a template parameter to a class + // (which could allow it to escape), as well as any presence of such a lambda + // in a return value (either from lambdas, or in c++14, auto functions). + // + // We check these lambdas' capture lists for raw pointers to refcounted types. + AstMatcher.addMatcher( + functionDecl(returns(recordType(hasDeclaration(cxxRecordDecl( + isLambdaDecl()).bind("decl"))))), + &RefCountedInsideLambda); + AstMatcher.addMatcher(lambdaExpr().bind("lambdaExpr"), + &RefCountedInsideLambda); + AstMatcher.addMatcher( + classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType( + recordType(hasDeclaration(cxxRecordDecl( + isLambdaDecl()).bind("decl")))))), + &RefCountedInsideLambda); + + // Older clang versions such as the ones used on the infra recognize these + // conversions as 'operator _Bool', but newer clang versions recognize these + // as 'operator bool'. + AstMatcher.addMatcher( + cxxMethodDecl(anyOf(hasName("operator bool"), hasName("operator _Bool"))) + .bind("node"), + &ExplicitOperatorBool); + + AstMatcher.addMatcher(cxxRecordDecl().bind("decl"), &NoDuplicateRefCntMember); + + AstMatcher.addMatcher( + classTemplateSpecializationDecl( + allOf(hasAnyTemplateArgument(refersToType(hasVTable())), + hasNeedsNoVTableTypeAttr())) + .bind("node"), + &NeedsNoVTableType); + + // Handle non-mem-movable template specializations + AstMatcher.addMatcher( + classTemplateSpecializationDecl( + allOf(needsMemMovableTemplateArg(), + hasAnyTemplateArgument(refersToType(isNonMemMovable())))) + .bind("specialization"), + &NonMemMovableTemplateArg); + + // Handle non-mem-movable members + AstMatcher.addMatcher( + cxxRecordDecl(needsMemMovableMembers()) + .bind("decl"), + &NonMemMovableMember); + + AstMatcher.addMatcher(cxxConstructorDecl(isInterestingImplicitCtor(), + ofClass(allOf(isConcreteClass(), + decl().bind("class"))), + unless(isMarkedImplicit())) + .bind("ctor"), + &ExplicitImplicit); + + AstMatcher.addMatcher(varDecl(hasType(autoNonAutoableType())).bind("node"), + &NoAutoType); + + AstMatcher.addMatcher( + cxxConstructorDecl(isExplicitMoveConstructor()).bind("node"), + &NoExplicitMoveConstructor); + + AstMatcher.addMatcher( + cxxConstructExpr( + hasDeclaration(cxxConstructorDecl(isCompilerProvidedCopyConstructor(), + ofClass(hasRefCntMember())))) + .bind("node"), + &RefCountedCopyConstructor); + + AstMatcher.addMatcher( + callExpr(isAssertAssignmentTestFunc()).bind("funcCall"), + &AssertAttribution); + + AstMatcher.addMatcher(varDecl(hasType(isRefPtr())).bind("decl"), + &KungFuDeathGrip); + + AstMatcher.addMatcher( + callExpr(isSnprintfLikeFunc(), + allOf(hasArgument(0, ignoringParenImpCasts(declRefExpr().bind("buffer"))), + anyOf(hasArgument(1, sizeOfExpr(hasIgnoringParenImpCasts(declRefExpr().bind("size")))), + hasArgument(1, integerLiteral().bind("immediate")), + hasArgument(1, declRefExpr(to(varDecl(hasType(isConstQualified()), + hasInitializer(integerLiteral().bind("constant"))))))))) + .bind("funcCall"), + &SprintfLiteral + ); + + AstMatcher.addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"), + &OverrideBaseCall); + + AstMatcher.addMatcher( + cxxMethodDecl(isNonVirtual(), isRequiredBaseMethod()).bind("method"), + &OverrideBaseCallUsage); + + AstMatcher.addMatcher( + functionDecl(anyOf(allOf(isDefinition(), + hasAncestor(classTemplateSpecializationDecl() + .bind("spec"))), + isDefinition())) + .bind("func"), + &NonParamInsideFunctionDecl); + AstMatcher.addMatcher( + lambdaExpr().bind("lambda"), + &NonParamInsideFunctionDecl); +} + +// These enum variants determine whether an allocation has occured in the code. +enum AllocationVariety { + AV_None, + AV_Global, + AV_Automatic, + AV_Temporary, + AV_Heap, +}; + +// XXX Currently the Decl* in the AutomaticTemporaryMap is unused, but it +// probably will be used at some point in the future, in order to produce better +// error messages. +typedef DenseMap + AutomaticTemporaryMap; +AutomaticTemporaryMap AutomaticTemporaries; + +void DiagnosticsMatcher::ScopeChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + + // There are a variety of different reasons why something could be allocated + AllocationVariety Variety = AV_None; + SourceLocation Loc; + QualType T; + + if (const ParmVarDecl *D = + Result.Nodes.getNodeAs("parm_vardecl")) { + if (D->hasUnparsedDefaultArg() || D->hasUninstantiatedDefaultArg()) { + return; + } + if (const Expr *Default = D->getDefaultArg()) { + if (const MaterializeTemporaryExpr *E = + dyn_cast(Default)) { + // We have just found a ParmVarDecl which has, as its default argument, + // a MaterializeTemporaryExpr. We mark that MaterializeTemporaryExpr as + // automatic, by adding it to the AutomaticTemporaryMap. + // Reporting on this type will occur when the MaterializeTemporaryExpr + // is matched against. + AutomaticTemporaries[E] = D; + } + } + return; + } + + // Determine the type of allocation which we detected + if (const VarDecl *D = Result.Nodes.getNodeAs("node")) { + if (D->hasGlobalStorage()) { + Variety = AV_Global; + } else { + Variety = AV_Automatic; + } + T = D->getType(); + Loc = D->getLocStart(); + } else if (const CXXNewExpr *E = Result.Nodes.getNodeAs("node")) { + // New allocates things on the heap. + // We don't consider placement new to do anything, as it doesn't actually + // allocate the storage, and thus gives us no useful information. + if (!isPlacementNew(E)) { + Variety = AV_Heap; + T = E->getAllocatedType(); + Loc = E->getLocStart(); + } + } else if (const MaterializeTemporaryExpr *E = + Result.Nodes.getNodeAs("node")) { + // Temporaries can actually have varying storage durations, due to temporary + // lifetime extension. We consider the allocation variety of this temporary + // to be the same as the allocation variety of its lifetime. + + // XXX We maybe should mark these lifetimes as being due to a temporary + // which has had its lifetime extended, to improve the error messages. + switch (E->getStorageDuration()) { + case SD_FullExpression: { + // Check if this temporary is allocated as a default argument! + // if it is, we want to pretend that it is automatic. + AutomaticTemporaryMap::iterator AutomaticTemporary = + AutomaticTemporaries.find(E); + if (AutomaticTemporary != AutomaticTemporaries.end()) { + Variety = AV_Automatic; + } else { + Variety = AV_Temporary; + } + } break; + case SD_Automatic: + Variety = AV_Automatic; + break; + case SD_Thread: + case SD_Static: + Variety = AV_Global; + break; + case SD_Dynamic: + assert(false && "I don't think that this ever should occur..."); + Variety = AV_Heap; + break; + } + T = E->getType().getUnqualifiedType(); + Loc = E->getLocStart(); + } else if (const CallExpr *E = Result.Nodes.getNodeAs("node")) { + T = E->getType()->getPointeeType(); + if (!T.isNull()) { + // This will always allocate on the heap, as the heapAllocator() check + // was made in the matcher + Variety = AV_Heap; + Loc = E->getLocStart(); + } + } + + // Error messages for incorrect allocations. + unsigned StackID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 only valid on the stack"); + unsigned GlobalID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 only valid as global"); + unsigned HeapID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 only valid on the heap"); + unsigned NonHeapID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 is not valid on the heap"); + unsigned NonTemporaryID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 is not valid in a temporary"); + + unsigned StackNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "value incorrectly allocated in an automatic variable"); + unsigned GlobalNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "value incorrectly allocated in a global variable"); + unsigned HeapNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "value incorrectly allocated on the heap"); + unsigned TemporaryNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "value incorrectly allocated in a temporary"); + + // Report errors depending on the annotations on the input types. + switch (Variety) { + case AV_None: + return; + + case AV_Global: + StackClass.reportErrorIfPresent(Diag, T, Loc, StackID, GlobalNoteID); + HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, GlobalNoteID); + break; + + case AV_Automatic: + GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, StackNoteID); + HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, StackNoteID); + break; + + case AV_Temporary: + GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, TemporaryNoteID); + HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, TemporaryNoteID); + NonTemporaryClass.reportErrorIfPresent(Diag, T, Loc, NonTemporaryID, + TemporaryNoteID); + break; + + case AV_Heap: + GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, HeapNoteID); + StackClass.reportErrorIfPresent(Diag, T, Loc, StackID, HeapNoteID); + NonHeapClass.reportErrorIfPresent(Diag, T, Loc, NonHeapID, HeapNoteID); + break; + } +} + +void DiagnosticsMatcher::ArithmeticArgChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "cannot pass an arithmetic expression of built-in types to %0"); + const Expr *Expression = Result.Nodes.getNodeAs("node"); + if (const CallExpr *Call = Result.Nodes.getNodeAs("call")) { + Diag.Report(Expression->getLocStart(), ErrorID) << Call->getDirectCallee(); + } else if (const CXXConstructExpr *Ctr = + Result.Nodes.getNodeAs("call")) { + Diag.Report(Expression->getLocStart(), ErrorID) << Ctr->getConstructor(); + } +} + +void DiagnosticsMatcher::TrivialCtorDtorChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "class %0 must have trivial constructors and destructors"); + const CXXRecordDecl *Node = Result.Nodes.getNodeAs("node"); + + // We need to accept non-constexpr trivial constructors as well. This occurs + // when a struct contains pod members, which will not be initialized. As + // constexpr values are initialized, the constructor is non-constexpr. + bool BadCtor = !(Node->hasConstexprDefaultConstructor() || + Node->hasTrivialDefaultConstructor()); + bool BadDtor = !Node->hasTrivialDestructor(); + if (BadCtor || BadDtor) + Diag.Report(Node->getLocStart(), ErrorID) << Node; +} + +void DiagnosticsMatcher::NaNExprChecker::run( + const MatchFinder::MatchResult &Result) { + if (!Result.Context->getLangOpts().CPlusPlus) { + // mozilla::IsNaN is not usable in C, so there is no point in issuing these + // warnings. + return; + } + + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "comparing a floating point value to itself for " + "NaN checking can lead to incorrect results"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "consider using mozilla::IsNaN instead"); + const BinaryOperator *Expression = Result.Nodes.getNodeAs( + "node"); + const DeclRefExpr *LHS = Result.Nodes.getNodeAs("lhs"); + const DeclRefExpr *RHS = Result.Nodes.getNodeAs("rhs"); + const ImplicitCastExpr *LHSExpr = dyn_cast( + Expression->getLHS()); + const ImplicitCastExpr *RHSExpr = dyn_cast( + Expression->getRHS()); + // The AST subtree that we are looking for will look like this: + // -BinaryOperator ==/!= + // |-ImplicitCastExpr LValueToRValue + // | |-DeclRefExpr + // |-ImplicitCastExpr LValueToRValue + // |-DeclRefExpr + // The check below ensures that we are dealing with the correct AST subtree + // shape, and + // also that both of the found DeclRefExpr's point to the same declaration. + if (LHS->getFoundDecl() == RHS->getFoundDecl() && LHSExpr && RHSExpr && + std::distance(LHSExpr->child_begin(), LHSExpr->child_end()) == 1 && + std::distance(RHSExpr->child_begin(), RHSExpr->child_end()) == 1 && + *LHSExpr->child_begin() == LHS && *RHSExpr->child_begin() == RHS) { + Diag.Report(Expression->getLocStart(), ErrorID); + Diag.Report(Expression->getLocStart(), NoteID); + } +} + +void DiagnosticsMatcher::NoAddRefReleaseOnReturnChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "%1 cannot be called on the return value of %0"); + const Stmt *Node = Result.Nodes.getNodeAs("node"); + const FunctionDecl *Func = Result.Nodes.getNodeAs("func"); + const MemberExpr *Member = Result.Nodes.getNodeAs("member"); + const CXXMethodDecl *Method = + dyn_cast(Member->getMemberDecl()); + + Diag.Report(Node->getLocStart(), ErrorID) << Func << Method; +} + +void DiagnosticsMatcher::RefCountedInsideLambdaChecker::run( + const MatchFinder::MatchResult &Result) { + Context = Result.Context; + static DenseSet CheckedDecls; + + const CXXRecordDecl *Lambda = Result.Nodes.getNodeAs("decl"); + + if (const LambdaExpr *OuterLambda = + Result.Nodes.getNodeAs("lambdaExpr")) { + const CXXMethodDecl *OpCall = OuterLambda->getCallOperator(); + QualType ReturnTy = OpCall->getReturnType(); + if (const CXXRecordDecl *Record = ReturnTy->getAsCXXRecordDecl()) { + Lambda = Record; + } + } + + if (!Lambda || !Lambda->isLambda()) { + return; + } + + // Don't report errors on the same declarations more than once. + if (CheckedDecls.count(Lambda)) { + return; + } + CheckedDecls.insert(Lambda); + + bool StrongRefToThisCaptured = false; + + for (const LambdaCapture& Capture : Lambda->captures()) { + // Check if any of the captures are ByRef. If they are, we have nothing to + // report, as it's OK to capture raw pointers to refcounted objects so long as + // the Lambda doesn't escape the current scope, which is required by ByRef + // captures already. + if (Capture.getCaptureKind() == LCK_ByRef) { + return; + } + + // Check if this capture is byvalue, and captures a strong reference to this. + // XXX: Do we want to make sure that this type which we are capturing is a "Smart Pointer" somehow? + if (!StrongRefToThisCaptured && + Capture.capturesVariable() && + Capture.getCaptureKind() == LCK_ByCopy) { + const VarDecl *Var = Capture.getCapturedVar(); + if (Var->hasInit()) { + const Stmt *Init = Var->getInit(); + + // Ignore single argument constructors, and trivial nodes. + while (true) { + auto NewInit = IgnoreImplicit(Init); + if (auto ConstructExpr = dyn_cast(NewInit)) { + if (ConstructExpr->getNumArgs() == 1) { + NewInit = ConstructExpr->getArg(0); + } + } + if (Init == NewInit) { + break; + } + Init = NewInit; + } + + if (isa(Init)) { + StrongRefToThisCaptured = true; + } + } + } + } + + // Now we can go through and produce errors for any captured variables or this pointers. + for (const LambdaCapture& Capture : Lambda->captures()) { + if (Capture.capturesVariable()) { + QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType(); + + if (!Pointee.isNull() && isClassRefCounted(Pointee)) { + emitDiagnostics(Capture.getLocation(), Capture.getCapturedVar()->getName(), Pointee); + return; + } + } + + // The situation with captures of `this` is more complex. All captures of + // `this` look the same-ish (they are LCK_This). We want to complain about + // captures of `this` where `this` is a refcounted type, and the capture is + // actually used in the body of the lambda (if the capture isn't used, then + // we don't care, because it's only being captured in order to give access + // to private methods). + // + // In addition, we don't complain about this, even if it is used, if it was + // captured implicitly when the LambdaCaptureDefault was LCD_ByRef, as that + // expresses the intent that the lambda won't leave the enclosing scope. + bool ImplicitByRefDefaultedCapture = + Capture.isImplicit() && Lambda->getLambdaCaptureDefault() == LCD_ByRef; + if (Capture.capturesThis() && + !ImplicitByRefDefaultedCapture && + !StrongRefToThisCaptured) { + ThisVisitor V(*this); + bool NotAborted = V.TraverseDecl(const_cast(Lambda->getLambdaCallOperator())); + if (!NotAborted) { + return; + } + } + } +} + +void DiagnosticsMatcher::RefCountedInsideLambdaChecker::emitDiagnostics( + SourceLocation Loc, StringRef Name, QualType Type) { + DiagnosticsEngine& Diag = Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Refcounted variable '%0' of type %1 cannot be captured by a lambda"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "Please consider using a smart pointer"); + + Diag.Report(Loc, ErrorID) << Name << Type; + Diag.Report(Loc, NoteID); +} + +bool DiagnosticsMatcher::RefCountedInsideLambdaChecker::ThisVisitor::VisitCXXThisExpr(CXXThisExpr *This) { + QualType Pointee = This->getType()->getPointeeType(); + if (!Pointee.isNull() && isClassRefCounted(Pointee)) { + Checker.emitDiagnostics(This->getLocStart(), "this", Pointee); + return false; + } + + return true; +} + +void DiagnosticsMatcher::ExplicitOperatorBoolChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "bad implicit conversion operator for %0"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "consider adding the explicit keyword to %0"); + const CXXConversionDecl *Method = + Result.Nodes.getNodeAs("node"); + const CXXRecordDecl *Clazz = Method->getParent(); + + if (!Method->isExplicitSpecified() && + !MozChecker::hasCustomAnnotation(Method, "moz_implicit") && + !ASTIsInSystemHeader(Method->getASTContext(), *Method) && + isInterestingDeclForImplicitConversion(Method)) { + Diag.Report(Method->getLocStart(), ErrorID) << Clazz; + Diag.Report(Method->getLocStart(), NoteID) << "'operator bool'"; + } +} + +void DiagnosticsMatcher::NoDuplicateRefCntMemberChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + const CXXRecordDecl *D = Result.Nodes.getNodeAs("decl"); + const FieldDecl *RefCntMember = getClassRefCntMember(D); + const FieldDecl *FoundRefCntBase = nullptr; + + if (!D->hasDefinition()) + return; + D = D->getDefinition(); + + // If we don't have an mRefCnt member, and we have less than 2 superclasses, + // we don't have to run this loop, as neither case will ever apply. + if (!RefCntMember && D->getNumBases() < 2) { + return; + } + + // Check every superclass for whether it has a base with a refcnt member, and + // warn for those which do + for (auto &Base : D->bases()) { + // Determine if this base class has an mRefCnt member + const FieldDecl *BaseRefCntMember = getBaseRefCntMember(Base.getType()); + + if (BaseRefCntMember) { + if (RefCntMember) { + // We have an mRefCnt, and superclass has an mRefCnt + unsigned Error = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "Refcounted record %0 has multiple mRefCnt members"); + unsigned Note1 = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "Superclass %0 also has an mRefCnt member"); + unsigned Note2 = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "Consider using the _INHERITED macros for AddRef and Release here"); + + Diag.Report(D->getLocStart(), Error) << D; + Diag.Report(BaseRefCntMember->getLocStart(), Note1) + << BaseRefCntMember->getParent(); + Diag.Report(RefCntMember->getLocStart(), Note2); + } + + if (FoundRefCntBase) { + unsigned Error = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "Refcounted record %0 has multiple superclasses with mRefCnt members"); + unsigned Note = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "Superclass %0 has an mRefCnt member"); + + // superclass has mRefCnt, and another superclass also has an mRefCnt + Diag.Report(D->getLocStart(), Error) << D; + Diag.Report(BaseRefCntMember->getLocStart(), Note) + << BaseRefCntMember->getParent(); + Diag.Report(FoundRefCntBase->getLocStart(), Note) + << FoundRefCntBase->getParent(); + } + + // Record that we've found a base with a mRefCnt member + FoundRefCntBase = BaseRefCntMember; + } + } +} + +void DiagnosticsMatcher::NeedsNoVTableTypeChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "%0 cannot be instantiated because %1 has a VTable"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "bad instantiation of %0 requested here"); + + const ClassTemplateSpecializationDecl *Specialization = + Result.Nodes.getNodeAs("node"); + + // Get the offending template argument + QualType Offender; + const TemplateArgumentList &Args = + Specialization->getTemplateInstantiationArgs(); + for (unsigned i = 0; i < Args.size(); ++i) { + Offender = Args[i].getAsType(); + if (typeHasVTable(Offender)) { + break; + } + } + + Diag.Report(Specialization->getLocStart(), ErrorID) << Specialization + << Offender; + Diag.Report(Specialization->getPointOfInstantiation(), NoteID) + << Specialization; +} + +void DiagnosticsMatcher::NonMemMovableTemplateArgChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "Cannot instantiate %0 with non-memmovable template argument %1"); + unsigned Note1ID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "instantiation of %0 requested here"); + + // Get the specialization + const ClassTemplateSpecializationDecl *Specialization = + Result.Nodes.getNodeAs("specialization"); + SourceLocation RequestLoc = Specialization->getPointOfInstantiation(); + + // Report an error for every template argument which is non-memmovable + const TemplateArgumentList &Args = + Specialization->getTemplateInstantiationArgs(); + for (unsigned i = 0; i < Args.size(); ++i) { + QualType ArgType = Args[i].getAsType(); + if (NonMemMovable.hasEffectiveAnnotation(ArgType)) { + Diag.Report(Specialization->getLocation(), ErrorID) << Specialization + << ArgType; + // XXX It would be really nice if we could get the instantiation stack + // information + // from Sema such that we could print a full template instantiation stack, + // however, + // it seems as though that information is thrown out by the time we get + // here so we + // can only report one level of template specialization (which in many + // cases won't + // be useful) + Diag.Report(RequestLoc, Note1ID) << Specialization; + NonMemMovable.dumpAnnotationReason(Diag, ArgType, RequestLoc); + } + } +} + +void DiagnosticsMatcher::NonMemMovableMemberChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "class %0 cannot have non-memmovable member %1 of type %2"); + + // Get the specialization + const CXXRecordDecl* Declaration = + Result.Nodes.getNodeAs("decl"); + + // Report an error for every member which is non-memmovable + for (const FieldDecl *Field : Declaration->fields()) { + QualType Type = Field->getType(); + if (NonMemMovable.hasEffectiveAnnotation(Type)) { + Diag.Report(Field->getLocation(), ErrorID) << Declaration + << Field + << Type; + NonMemMovable.dumpAnnotationReason(Diag, Type, Declaration->getLocation()); + } + } +} + +void DiagnosticsMatcher::ExplicitImplicitChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "bad implicit conversion constructor for %0"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "consider adding the explicit keyword to the constructor"); + + // We've already checked everything in the matcher, so we just have to report + // the error. + + const CXXConstructorDecl *Ctor = + Result.Nodes.getNodeAs("ctor"); + const CXXRecordDecl *Declaration = + Result.Nodes.getNodeAs("class"); + + Diag.Report(Ctor->getLocation(), ErrorID) << Declaration->getDeclName(); + Diag.Report(Ctor->getLocation(), NoteID); +} + +void DiagnosticsMatcher::NoAutoTypeChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Cannot use auto to declare a variable of type %0"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "Please write out this type explicitly"); + + const VarDecl *D = Result.Nodes.getNodeAs("node"); + + Diag.Report(D->getLocation(), ErrorID) << D->getType(); + Diag.Report(D->getLocation(), NoteID); +} + +void DiagnosticsMatcher::NoExplicitMoveConstructorChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Move constructors may not be marked explicit"); + + // Everything we needed to know was checked in the matcher - we just report + // the error here + const CXXConstructorDecl *D = + Result.Nodes.getNodeAs("node"); + + Diag.Report(D->getLocation(), ErrorID); +} + +void DiagnosticsMatcher::RefCountedCopyConstructorChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Invalid use of compiler-provided copy constructor " + "on refcounted type"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "The default copy constructor also copies the " + "default mRefCnt property, leading to reference " + "count imbalance issues. Please provide your own " + "copy constructor which only copies the fields which " + "need to be copied"); + + // Everything we needed to know was checked in the matcher - we just report + // the error here + const CXXConstructExpr *E = Result.Nodes.getNodeAs("node"); + + Diag.Report(E->getLocation(), ErrorID); + Diag.Report(E->getLocation(), NoteID); +} + +void DiagnosticsMatcher::AssertAssignmentChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned AssignInsteadOfComp = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Forbidden assignment in assert expression"); + const CallExpr *FuncCall = Result.Nodes.getNodeAs("funcCall"); + + if (FuncCall && hasSideEffectAssignment(FuncCall)) { + Diag.Report(FuncCall->getLocStart(), AssignInsteadOfComp); + } +} + +void DiagnosticsMatcher::KungFuDeathGripChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "Unused \"kungFuDeathGrip\" %0 objects constructed from %1 are prohibited"); + + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "Please switch all accesses to this %0 to go through '%1', or explicitly pass '%1' to `mozilla::Unused`"); + + const VarDecl *D = Result.Nodes.getNodeAs("decl"); + if (D->isReferenced() || !D->hasLocalStorage() || !D->hasInit()) { + return; + } + + // Not interested in parameters. + if (isa(D) || isa(D)) { + return; + } + + const Expr *E = IgnoreImplicit(D->getInit()); + const CXXConstructExpr *CE = dyn_cast(E); + if (CE && CE->getNumArgs() == 0) { + // We don't report an error when we construct and don't use a nsCOMPtr / + // nsRefPtr with no arguments. We don't report it because the error is not + // related to the current check. In the future it may be reported through a + // more generic mechanism. + return; + } + + // We don't want to look at the single argument conversion constructors + // which are inbetween the declaration and the actual object which we are + // assigning into the nsCOMPtr/RefPtr. To do this, we repeatedly + // IgnoreImplicit, then look at the expression. If it is one of these + // conversion constructors, we ignore it and continue to dig. + while ((CE = dyn_cast(E)) && CE->getNumArgs() == 1) { + E = IgnoreImplicit(CE->getArg(0)); + } + + // We allow taking a kungFuDeathGrip of `this` because it cannot change + // beneath us, so calling directly through `this` is OK. This is the same + // for local variable declarations. + // + // We also don't complain about unused RefPtrs which are constructed from + // the return value of a new expression, as these are required in order to + // immediately destroy the value created (which was presumably created for + // its side effects), and are not used as a death grip. + if (isa(E) || isa(E) || isa(E)) { + return; + } + + // These types are assigned into nsCOMPtr and RefPtr for their side effects, + // and not as a kungFuDeathGrip. We don't want to consider RefPtr and nsCOMPtr + // types which are initialized with these types as errors. + const TagDecl *TD = E->getType()->getAsTagDecl(); + if (TD && TD->getIdentifier()) { + static const char *IgnoreTypes[] = { + "already_AddRefed", + "nsGetServiceByCID", + "nsGetServiceByCIDWithError", + "nsGetServiceByContractID", + "nsGetServiceByContractIDWithError", + "nsCreateInstanceByCID", + "nsCreateInstanceByContractID", + "nsCreateInstanceFromFactory", + }; + + for (uint32_t i = 0; i < sizeof(IgnoreTypes) / sizeof(IgnoreTypes[0]); ++i) { + if (TD->getName() == IgnoreTypes[i]) { + return; + } + } + } + + // Report the error + const char *ErrThing; + const char *NoteThing; + if (isa(E)) { + ErrThing = "members"; + NoteThing = "member"; + } else { + ErrThing = "temporary values"; + NoteThing = "value"; + } + + // We cannot provide the note if we don't have an initializer + Diag.Report(D->getLocStart(), ErrorID) << D->getType() << ErrThing; + Diag.Report(E->getLocStart(), NoteID) << NoteThing << getNameChecked(D); +} + +void DiagnosticsMatcher::SprintfLiteralChecker::run( + const MatchFinder::MatchResult &Result) { + if (!Result.Context->getLangOpts().CPlusPlus) { + // SprintfLiteral is not usable in C, so there is no point in issuing these + // warnings. + return; + } + + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Use %1 instead of %0 when writing into a character array."); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "This will prevent passing in the wrong size to %0 accidentally."); + + const CallExpr *D = Result.Nodes.getNodeAs("funcCall"); + + StringRef Name = D->getDirectCallee()->getName(); + const char *Replacement; + if (Name == "snprintf") { + Replacement = "SprintfLiteral"; + } else { + assert(Name == "vsnprintf"); + Replacement = "VsprintfLiteral"; + } + + const DeclRefExpr *Buffer = Result.Nodes.getNodeAs("buffer"); + const DeclRefExpr *Size = Result.Nodes.getNodeAs("size"); + if (Size) { + // Match calls like snprintf(x, sizeof(x), ...). + if (Buffer->getFoundDecl() != Size->getFoundDecl()) { + return; + } + + Diag.Report(D->getLocStart(), ErrorID) << Name << Replacement; + Diag.Report(D->getLocStart(), NoteID) << Name; + return; + } + + const QualType QType = Buffer->getType(); + const ConstantArrayType *Type = dyn_cast(QType.getTypePtrOrNull()); + if (Type) { + // Match calls like snprintf(x, 100, ...), where x is int[100]; + const IntegerLiteral *Literal = Result.Nodes.getNodeAs("immediate"); + if (!Literal) { + // Match calls like: const int y = 100; snprintf(x, y, ...); + Literal = Result.Nodes.getNodeAs("constant"); + } + + if (Type->getSize().ule(Literal->getValue())) { + Diag.Report(D->getLocStart(), ErrorID) << Name << Replacement; + Diag.Report(D->getLocStart(), NoteID) << Name; + } + } +} + +bool DiagnosticsMatcher::OverrideBaseCallChecker::isRequiredBaseMethod( + const CXXMethodDecl *Method) { + return MozChecker::hasCustomAnnotation(Method, "moz_required_base_method"); +} + +void DiagnosticsMatcher::OverrideBaseCallChecker::evaluateExpression( + const Stmt *StmtExpr, std::list &MethodList) { + // Continue while we have methods in our list + if (!MethodList.size()) { + return; + } + + if (auto MemberFuncCall = dyn_cast(StmtExpr)) { + if (auto Method = dyn_cast( + MemberFuncCall->getDirectCallee())) { + findBaseMethodCall(Method, MethodList); + } + } + + for (auto S : StmtExpr->children()) { + if (S) { + evaluateExpression(S, MethodList); + } + } +} + +void DiagnosticsMatcher::OverrideBaseCallChecker::getRequiredBaseMethod( + const CXXMethodDecl *Method, + std::list& MethodsList) { + + if (isRequiredBaseMethod(Method)) { + MethodsList.push_back(Method); + } else { + // Loop through all it's base methods. + for (auto BaseMethod = Method->begin_overridden_methods(); + BaseMethod != Method->end_overridden_methods(); BaseMethod++) { + getRequiredBaseMethod(*BaseMethod, MethodsList); + } + } +} + +void DiagnosticsMatcher::OverrideBaseCallChecker::findBaseMethodCall( + const CXXMethodDecl* Method, + std::list& MethodsList) { + + MethodsList.remove(Method); + // Loop also through all it's base methods; + for (auto BaseMethod = Method->begin_overridden_methods(); + BaseMethod != Method->end_overridden_methods(); BaseMethod++) { + findBaseMethodCall(*BaseMethod, MethodsList); + } +} + +void DiagnosticsMatcher::OverrideBaseCallChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned OverrideBaseCallCheckID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "Method %0 must be called in all overrides, but is not called in " + "this override defined for class %1"); + const CXXRecordDecl *Decl = Result.Nodes.getNodeAs("class"); + + // Loop through the methods and look for the ones that are overridden. + for (auto Method : Decl->methods()) { + // If this method doesn't override other methods or it doesn't have a body, + // continue to the next declaration. + if (!Method->size_overridden_methods() || !Method->hasBody()) { + continue; + } + + // Preferred the usage of list instead of vector in order to avoid + // calling erase-remove when deleting items + std::list MethodsList; + // For each overridden method push it to a list if it meets our + // criteria + for (auto BaseMethod = Method->begin_overridden_methods(); + BaseMethod != Method->end_overridden_methods(); BaseMethod++) { + getRequiredBaseMethod(*BaseMethod, MethodsList); + } + + // If no method has been found then no annotation was used + // so checking is not needed + if (!MethodsList.size()) { + continue; + } + + // Loop through the body of our method and search for calls to + // base methods + evaluateExpression(Method->getBody(), MethodsList); + + // If list is not empty pop up errors + for (auto BaseMethod : MethodsList) { + Diag.Report(Method->getLocation(), OverrideBaseCallCheckID) + << BaseMethod->getQualifiedNameAsString() + << Decl->getName(); + } + } +} + +void DiagnosticsMatcher::OverrideBaseCallUsageChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods"); + const CXXMethodDecl *Method = Result.Nodes.getNodeAs("method"); + + Diag.Report(Method->getLocation(), ErrorID); +} + +void DiagnosticsMatcher::NonParamInsideFunctionDeclChecker::run( + const MatchFinder::MatchResult &Result) { + static DenseSet CheckedFunctionDecls; + + const FunctionDecl *func = Result.Nodes.getNodeAs("func"); + if (!func) { + const LambdaExpr *lambda = Result.Nodes.getNodeAs("lambda"); + if (lambda) { + func = lambda->getCallOperator(); + } + } + + if (!func) { + return; + } + + if (func->isDeleted()) { + return; + } + + // Don't report errors on the same declarations more than once. + if (CheckedFunctionDecls.count(func)) { + return; + } + CheckedFunctionDecls.insert(func); + + const ClassTemplateSpecializationDecl *Spec = + Result.Nodes.getNodeAs("spec"); + + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Type %0 must not be used as parameter"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "Please consider passing a const reference instead"); + unsigned SpecNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "The bad argument was passed to %0 here"); + + for (ParmVarDecl *p : func->parameters()) { + QualType T = p->getType().withoutLocalFastQualifiers(); + if (NonParam.hasEffectiveAnnotation(T)) { + Diag.Report(p->getLocation(), ErrorID) << T; + Diag.Report(p->getLocation(), NoteID); + + if (Spec) { + Diag.Report(Spec->getPointOfInstantiation(), SpecNoteID) + << Spec->getSpecializedTemplate(); + } + } + } +} + +class MozCheckAction : public PluginASTAction { +public: + ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, + StringRef FileName) override { +#if CLANG_VERSION_FULL >= 306 + std::unique_ptr Checker(llvm::make_unique(CI)); + ASTConsumerPtr Other(Checker->getOtherConsumer()); + + std::vector Consumers; + Consumers.push_back(std::move(Checker)); + Consumers.push_back(std::move(Other)); + return llvm::make_unique(std::move(Consumers)); +#else + MozChecker *Checker = new MozChecker(CI); + + ASTConsumer *Consumers[] = {Checker, Checker->getOtherConsumer()}; + return new MultiplexConsumer(Consumers); +#endif + } + + bool ParseArgs(const CompilerInstance &CI, + const std::vector &Args) override { + return true; + } +}; +} + +static FrontendPluginRegistry::Add X("moz-check", + "check moz action"); +// Export the registry on Windows. +#ifdef LLVM_EXPORT_REGISTRY +LLVM_EXPORT_REGISTRY(FrontendPluginRegistry) +#endif diff --git a/build/clang-plugin/moz.build b/build/clang-plugin/moz.build new file mode 100644 index 000000000..d9b20cd90 --- /dev/null +++ b/build/clang-plugin/moz.build @@ -0,0 +1,23 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SharedLibrary('clang-plugin') + +SOURCES += [ + 'clang-plugin.cpp', +] + +DISABLE_STL_WRAPPING = True +NO_VISIBILITY_FLAGS = True + +# libc++ is required to build plugins against clang on OS X. +if CONFIG['HOST_OS_ARCH'] == 'Darwin': + CXXFLAGS += ['-stdlib=libc++'] + LDFLAGS += ['-lc++'] + +DIRS += [ + 'tests', +] diff --git a/build/clang-plugin/tests/Makefile.in b/build/clang-plugin/tests/Makefile.in new file mode 100644 index 000000000..74faf065c --- /dev/null +++ b/build/clang-plugin/tests/Makefile.in @@ -0,0 +1,18 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Build without any warning flags, and with clang verify flag for a +# syntax-only build (no codegen), without a limit on the number of errors. +OS_CFLAGS := $(filter-out -W%,$(OS_CFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0 -std=c11 +OS_CXXFLAGS := $(filter-out -W%,$(OS_CXXFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0 + +include $(topsrcdir)/config/rules.mk + +target:: $(OBJS) + +# We don't actually build anything. +.PHONY: $(OBJS) + +# Don't actually build a library, since we don't actually build objects. +$(LIBRARY): EXPAND_LIBS_GEN=true diff --git a/build/clang-plugin/tests/NonParameterTestCases.h b/build/clang-plugin/tests/NonParameterTestCases.h new file mode 100644 index 000000000..d38a14d94 --- /dev/null +++ b/build/clang-plugin/tests/NonParameterTestCases.h @@ -0,0 +1,61 @@ +MAYBE_STATIC void raw(Param x) {} + +MAYBE_STATIC void raw(NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamUnion x) {} //expected-error {{Type 'NonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamClass x) {} //expected-error {{Type 'NonParamClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamEnum x) {} //expected-error {{Type 'NonParamEnum' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamEnumClass x) {} //expected-error {{Type 'NonParamEnumClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(HasNonParamStruct x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(HasNonParamUnion x) {} //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(HasNonParamStructUnion x) {} //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +MAYBE_STATIC void const_(const NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamUnion x) {} //expected-error {{Type 'NonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamClass x) {} //expected-error {{Type 'NonParamClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamEnum x) {} //expected-error {{Type 'NonParamEnum' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamEnumClass x) {} //expected-error {{Type 'NonParamEnumClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const HasNonParamStruct x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const HasNonParamUnion x) {} //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const HasNonParamStructUnion x) {} //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +MAYBE_STATIC void array(NonParam x[]) {} +MAYBE_STATIC void array(NonParamUnion x[]) {} +MAYBE_STATIC void array(NonParamClass x[]) {} +MAYBE_STATIC void array(NonParamEnum x[]) {} +MAYBE_STATIC void array(NonParamEnumClass x[]) {} +MAYBE_STATIC void array(HasNonParamStruct x[]) {} +MAYBE_STATIC void array(HasNonParamUnion x[]) {} +MAYBE_STATIC void array(HasNonParamStructUnion x[]) {} + +MAYBE_STATIC void ptr(NonParam* x) {} +MAYBE_STATIC void ptr(NonParamUnion* x) {} +MAYBE_STATIC void ptr(NonParamClass* x) {} +MAYBE_STATIC void ptr(NonParamEnum* x) {} +MAYBE_STATIC void ptr(NonParamEnumClass* x) {} +MAYBE_STATIC void ptr(HasNonParamStruct* x) {} +MAYBE_STATIC void ptr(HasNonParamUnion* x) {} +MAYBE_STATIC void ptr(HasNonParamStructUnion* x) {} + +MAYBE_STATIC void ref(NonParam& x) {} +MAYBE_STATIC void ref(NonParamUnion& x) {} +MAYBE_STATIC void ref(NonParamClass& x) {} +MAYBE_STATIC void ref(NonParamEnum& x) {} +MAYBE_STATIC void ref(NonParamEnumClass& x) {} +MAYBE_STATIC void ref(HasNonParamStruct& x) {} +MAYBE_STATIC void ref(HasNonParamUnion& x) {} +MAYBE_STATIC void ref(HasNonParamStructUnion& x) {} + +MAYBE_STATIC void constRef(const NonParam& x) {} +MAYBE_STATIC void constRef(const NonParamUnion& x) {} +MAYBE_STATIC void constRef(const NonParamClass& x) {} +MAYBE_STATIC void constRef(const NonParamEnum& x) {} +MAYBE_STATIC void constRef(const NonParamEnumClass& x) {} +MAYBE_STATIC void constRef(const HasNonParamStruct& x) {} +MAYBE_STATIC void constRef(const HasNonParamUnion& x) {} +MAYBE_STATIC void constRef(const HasNonParamStructUnion& x) {} + +MAYBE_STATIC inline void inlineRaw(NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamUnion x) {} //expected-error {{Type 'NonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamClass x) {} //expected-error {{Type 'NonParamClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamEnum x) {} //expected-error {{Type 'NonParamEnum' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamEnumClass x) {} //expected-error {{Type 'NonParamEnumClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} diff --git a/build/clang-plugin/tests/TestAssertWithAssignment.cpp b/build/clang-plugin/tests/TestAssertWithAssignment.cpp new file mode 100644 index 000000000..f0f049e4a --- /dev/null +++ b/build/clang-plugin/tests/TestAssertWithAssignment.cpp @@ -0,0 +1,68 @@ +#include "mozilla/MacroArgs.h" + +static __attribute__((always_inline)) bool MOZ_AssertAssignmentTest(bool expr) { + return expr; +} + +#define MOZ_UNLIKELY(x) (__builtin_expect(!!(x), 0)) +#define MOZ_CRASH() do { } while(0) +#define MOZ_CHECK_ASSERT_ASSIGNMENT(expr) MOZ_AssertAssignmentTest(!!(expr)) + +#define MOZ_ASSERT_HELPER1(expr) \ + do { \ + if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \ + MOZ_CRASH();\ + } \ + } while(0) \ + +/* Now the two-argument form. */ +#define MOZ_ASSERT_HELPER2(expr, explain) \ + do { \ + if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \ + MOZ_CRASH();\ + } \ + } while(0) \ + +#define MOZ_RELEASE_ASSERT_GLUE(a, b) a b +#define MOZ_RELEASE_ASSERT(...) \ + MOZ_RELEASE_ASSERT_GLUE( \ + MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \ + (__VA_ARGS__)) + +#define MOZ_ASSERT(...) MOZ_RELEASE_ASSERT(__VA_ARGS__) + +void FunctionTest(int p) { + MOZ_ASSERT(p = 1); // expected-error {{Forbidden assignment in assert expression}} +} + +void FunctionTest2(int p) { + MOZ_ASSERT(((p = 1))); // expected-error {{Forbidden assignment in assert expression}} +} + +void FunctionTest3(int p) { + MOZ_ASSERT(p != 3); +} + +class TestOverloading { + int value; +public: + explicit TestOverloading(int _val) : value(_val) {} + // different operators + explicit operator bool() const { return true; } + TestOverloading& operator=(const int _val) { value = _val; return *this; } + + int& GetInt() {return value;} +}; + +void TestOverloadingFunc() { + TestOverloading p(2); + int f; + + MOZ_ASSERT(p); + MOZ_ASSERT(p = 3); // expected-error {{Forbidden assignment in assert expression}} + MOZ_ASSERT(p, "p is not valid"); + MOZ_ASSERT(p = 3, "p different than 3"); // expected-error {{Forbidden assignment in assert expression}} + MOZ_ASSERT(p.GetInt() = 2); // expected-error {{Forbidden assignment in assert expression}} + MOZ_ASSERT(p.GetInt() == 2); + MOZ_ASSERT(p.GetInt() == 2, f = 3); +} diff --git a/build/clang-plugin/tests/TestBadImplicitConversionCtor.cpp b/build/clang-plugin/tests/TestBadImplicitConversionCtor.cpp new file mode 100644 index 000000000..ca2472582 --- /dev/null +++ b/build/clang-plugin/tests/TestBadImplicitConversionCtor.cpp @@ -0,0 +1,50 @@ +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +struct Foo { + Foo(int); // expected-error {{bad implicit conversion constructor for 'Foo'}} expected-note {{consider adding the explicit keyword to the constructor}} + Foo(int, char=0); // expected-error {{bad implicit conversion constructor for 'Foo'}} expected-note {{consider adding the explicit keyword to the constructor}} + Foo(...); // expected-error {{bad implicit conversion constructor for 'Foo'}} expected-note {{consider adding the explicit keyword to the constructor}} + template + Foo(float); // expected-error {{bad implicit conversion constructor for 'Foo'}} expected-note {{consider adding the explicit keyword to the constructor}} + Foo(int, unsigned); + Foo(Foo&); + Foo(const Foo&); + Foo(volatile Foo&); + Foo(const volatile Foo&); + Foo(Foo&&); + Foo(const Foo&&); + Foo(volatile Foo&&); + Foo(const volatile Foo&&); +}; + +struct Bar { + explicit Bar(int); + explicit Bar(int, char=0); + explicit Bar(...); +}; + +struct Baz { + MOZ_IMPLICIT Baz(int); + MOZ_IMPLICIT Baz(int, char=0); + MOZ_IMPLICIT Baz(...); +}; + +struct Barn { + Barn(int) = delete; + Barn(int, char=0) = delete; + Barn(...) = delete; +}; + +struct Abstract { + Abstract(int); + Abstract(int, char=0); + Abstract(...); + virtual void f() = 0; +}; + +template +struct Template { + Template(int); // expected-error {{bad implicit conversion constructor for 'Template'}} expected-note {{consider adding the explicit keyword to the constructor}} + template + Template(float); // expected-error {{bad implicit conversion constructor for 'Template'}} expected-note {{consider adding the explicit keyword to the constructor}} +}; diff --git a/build/clang-plugin/tests/TestCustomHeap.cpp b/build/clang-plugin/tests/TestCustomHeap.cpp new file mode 100644 index 000000000..9514ff2c4 --- /dev/null +++ b/build/clang-plugin/tests/TestCustomHeap.cpp @@ -0,0 +1,28 @@ +#define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class"))) +#define MOZ_HEAP_ALLOCATOR \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((annotate("moz_heap_allocator"))) \ + _Pragma("GCC diagnostic pop") + +#include +#include + +struct MOZ_NONHEAP_CLASS X { +}; + +void *operator new(size_t x, int qual) MOZ_HEAP_ALLOCATOR { + return ::operator new(x); +} + +template +T *customAlloc() MOZ_HEAP_ALLOCATOR { + T *arg = static_cast(malloc(sizeof(T))); + return new (arg) T(); +} + +template +void misuseX(T q) { + X *foo = customAlloc(); // expected-error {{variable of type 'X' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + X *foo2 = new (100) X(); // expected-error {{variable of type 'X' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} +} diff --git a/build/clang-plugin/tests/TestExplicitOperatorBool.cpp b/build/clang-plugin/tests/TestExplicitOperatorBool.cpp new file mode 100644 index 000000000..bc4b43a7d --- /dev/null +++ b/build/clang-plugin/tests/TestExplicitOperatorBool.cpp @@ -0,0 +1,11 @@ +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +struct Bad { + operator bool(); // expected-error {{bad implicit conversion operator for 'Bad'}} expected-note {{consider adding the explicit keyword to 'operator bool'}} +}; +struct Good { + explicit operator bool(); +}; +struct Okay { + MOZ_IMPLICIT operator bool(); +}; diff --git a/build/clang-plugin/tests/TestGlobalClass.cpp b/build/clang-plugin/tests/TestGlobalClass.cpp new file mode 100644 index 000000000..1825b9707 --- /dev/null +++ b/build/clang-plugin/tests/TestGlobalClass.cpp @@ -0,0 +1,52 @@ +#define MOZ_GLOBAL_CLASS __attribute__((annotate("moz_global_class"))) +#include + +struct MOZ_GLOBAL_CLASS Global { + int i; + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template +struct MOZ_GLOBAL_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void misuseGlobalClass(int len) { + Global notValid; // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}} + Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}} expected-note {{value incorrectly allocated in an automatic variable}} + static Global valid; + static Global alsoValid[2]; + + gobble(¬Valid); + gobble(&valid); + gobble(&alsoValid[0]); + + gobble(new Global); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}} + gobble(new Global[10]); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}} + gobble(new TemplateClass); // expected-error {{variable of type 'TemplateClass' only valid as global}} expected-note {{value incorrectly allocated on the heap}} + gobble(len <= 5 ? &valid : new Global); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}} + + char buffer[sizeof(Global)]; + gobble(new (buffer) Global); +} + +Global valid; +struct RandomClass { + Global nonstaticMember; // expected-note {{'RandomClass' is a global type because member 'nonstaticMember' is a global type 'Global'}} + static Global staticMember; +}; +struct MOZ_GLOBAL_CLASS RandomGlobalClass { + Global nonstaticMember; + static Global staticMember; +}; + +struct BadInherit : Global {}; // expected-note {{'BadInherit' is a global type because it inherits from a global type 'Global'}} +struct MOZ_GLOBAL_CLASS GoodInherit : Global {}; + +void misuseGlobalClassEvenMore(int len) { + BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}} + RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}} +} diff --git a/build/clang-plugin/tests/TestHeapClass.cpp b/build/clang-plugin/tests/TestHeapClass.cpp new file mode 100644 index 000000000..36e762973 --- /dev/null +++ b/build/clang-plugin/tests/TestHeapClass.cpp @@ -0,0 +1,64 @@ +#define MOZ_HEAP_CLASS __attribute__((annotate("moz_heap_class"))) +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +#include + +struct MOZ_HEAP_CLASS Heap { + int i; + Heap() {} + MOZ_IMPLICIT Heap(int a) {} + Heap(int a, int b) {} + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template +struct MOZ_HEAP_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void gobbleref(const Heap&) { } + +void misuseHeapClass(int len) { + Heap invalid; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}} + Heap alsoInvalid[2]; // expected-error {{variable of type 'Heap [2]' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}} expected-note {{'Heap [2]' is a heap type because it is an array of heap type 'Heap'}} + static Heap invalidStatic; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} + static Heap alsoInvalidStatic[2]; // expected-error {{variable of type 'Heap [2]' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} expected-note {{'Heap [2]' is a heap type because it is an array of heap type 'Heap'}} + + gobble(&invalid); + gobble(&invalidStatic); + gobble(&alsoInvalid[0]); + + gobbleref(Heap()); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(Heap(10, 20)); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(Heap(10)); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(10); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}} + + gobble(new Heap); + gobble(new Heap[10]); + gobble(new TemplateClass); + gobble(len <= 5 ? &invalid : new Heap); + + char buffer[sizeof(Heap)]; + gobble(new (buffer) Heap); +} + +Heap invalidStatic; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} +struct RandomClass { + Heap nonstaticMember; // expected-note {{'RandomClass' is a heap type because member 'nonstaticMember' is a heap type 'Heap'}} + static Heap staticMember; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} +}; +struct MOZ_HEAP_CLASS RandomHeapClass { + Heap nonstaticMember; + static Heap staticMember; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} +}; + +struct BadInherit : Heap {}; // expected-note {{'BadInherit' is a heap type because it inherits from a heap type 'Heap'}} +struct MOZ_HEAP_CLASS GoodInherit : Heap {}; + +void useStuffWrongly() { + BadInherit i; // expected-error {{variable of type 'BadInherit' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}} + RandomClass r; // expected-error {{variable of type 'RandomClass' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}} +} diff --git a/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp b/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp new file mode 100644 index 000000000..a46ff39fd --- /dev/null +++ b/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp @@ -0,0 +1,46 @@ +#define MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS \ + __attribute__((annotate("moz_inherit_type_annotations_from_template_args"))) +#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) +#define MOZ_NON_MEMMOVABLE __attribute__((annotate("moz_non_memmovable"))) +#define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type"))) + +class Normal {}; +class MOZ_STACK_CLASS Stack {}; +class IndirectStack : Stack {}; // expected-note {{'IndirectStack' is a stack type because it inherits from a stack type 'Stack'}} +class ContainsStack { Stack m; }; // expected-note {{'ContainsStack' is a stack type because member 'm' is a stack type 'Stack'}} +class MOZ_NON_MEMMOVABLE Pointery {}; +class IndirectPointery : Pointery {}; // expected-note {{'IndirectPointery' is a non-memmove()able type because it inherits from a non-memmove()able type 'Pointery'}} +class ContainsPointery { Pointery m; }; // expected-note {{'ContainsPointery' is a non-memmove()able type because member 'm' is a non-memmove()able type 'Pointery'}} + +template +class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Template {}; // expected-note-re 5 {{'Template<{{.*}}>' is a stack type because it has a template argument stack type '{{.*}}'}} expected-note-re 5 {{'Template<{{.*}}>' is a non-memmove()able type because it has a template argument non-memmove()able type '{{.*}}'}} +class IndirectTemplate : Template {}; // expected-note {{'IndirectTemplate' is a stack type because it inherits from a stack type 'Template'}} +class ContainsTemplate { Template m; }; // expected-note {{'ContainsTemplate' is a stack type because member 'm' is a stack type 'Template'}} + +static Template a; // expected-error {{variable of type 'Template' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +static Template b; // expected-error {{variable of type 'Template' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +static Template c; // expected-error {{variable of type 'Template' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +static IndirectTemplate d; // expected-error {{variable of type 'IndirectTemplate' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +static ContainsTemplate e; // expected-error {{variable of type 'ContainsTemplate' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +static Template f; + +template +class MOZ_NEEDS_MEMMOVABLE_TYPE Mover { // expected-error-re 8 {{Cannot instantiate 'Mover<{{.*}}>' with non-memmovable template argument '{{.*}}'}} + char mForceInstantiation[sizeof(T)]; +}; +class IndirectTemplatePointery : Template {}; // expected-note {{'IndirectTemplatePointery' is a non-memmove()able type because it inherits from a non-memmove()able type 'Template'}} +class ContainsTemplatePointery { Template m; }; // expected-note {{'ContainsTemplatePointery' is a non-memmove()able type because member 'm' is a non-memmove()able type 'Template'}} + +static Mover> n; // expected-note {{instantiation of 'Mover >' requested here}} +static Mover> o; // expected-note {{instantiation of 'Mover >' requested here}} +static Mover> p; // expected-note {{instantiation of 'Mover >' requested here}} +static Mover q; // expected-note {{instantiation of 'Mover' requested here}} +static Mover r; // expected-note {{instantiation of 'Mover' requested here}} +static Mover> s; + +template +class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS ManyTs {}; // expected-note-re 3 {{'ManyTs<{{.*}}>' is a non-memmove()able type because it has a template argument non-memmove()able type '{{.*}}'}} + +static Mover> t; // expected-note {{instantiation of 'Mover >' requested here}} +static Mover> u; // expected-note {{instantiation of 'Mover >' requested here}} +static Mover> v; // expected-note {{instantiation of 'Mover >' requested here}} diff --git a/build/clang-plugin/tests/TestKungFuDeathGrip.cpp b/build/clang-plugin/tests/TestKungFuDeathGrip.cpp new file mode 100644 index 000000000..0b94d8a88 --- /dev/null +++ b/build/clang-plugin/tests/TestKungFuDeathGrip.cpp @@ -0,0 +1,107 @@ +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +template +class already_AddRefed { +public: + already_AddRefed(); + T* mPtr; +}; + +template +class RefPtr { +public: + RefPtr(); + MOZ_IMPLICIT RefPtr(T* aIn); + MOZ_IMPLICIT RefPtr(already_AddRefed aIn); + ~RefPtr(); + T* mPtr; +}; + +template +class nsCOMPtr { +public: + nsCOMPtr(); + MOZ_IMPLICIT nsCOMPtr(T* aIn); + MOZ_IMPLICIT nsCOMPtr(already_AddRefed aIn); + ~nsCOMPtr(); + T* mPtr; +}; + +class Type { +public: + static nsCOMPtr someStaticCOMPtr; + + void f(nsCOMPtr ignoredArgument, Type *param) { + nsCOMPtr never_referenced; + nsCOMPtr kfdg_t1(this); + nsCOMPtr kfdg_t2 = this; + + nsCOMPtr kfdg_m1(p); // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m1', or explicitly pass 'kfdg_m1' to `mozilla::Unused`}} + nsCOMPtr kfdg_m2 = p; // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m2', or explicitly pass 'kfdg_m2' to `mozilla::Unused`}} + nsCOMPtr kfdg_m3(p); + kfdg_m3.mPtr->f(nullptr, nullptr); + nsCOMPtr kfdg_m4 = p; + kfdg_m4.mPtr->f(nullptr, nullptr); + + nsCOMPtr kfdg_a1((already_AddRefed())); + nsCOMPtr kfdg_a2 = already_AddRefed(); + + nsCOMPtr kfdg_p1(param); + nsCOMPtr kfdg_p2 = param; + + + RefPtr never_referenced2; + RefPtr kfdg_t3(this); + RefPtr kfdg_t4 = this; + + RefPtr kfdg_m5(p); // expected-error {{Unused "kungFuDeathGrip" 'RefPtr' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m5', or explicitly pass 'kfdg_m5' to `mozilla::Unused`}} + RefPtr kfdg_m6 = p; // expected-error {{Unused "kungFuDeathGrip" 'RefPtr' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m6', or explicitly pass 'kfdg_m6' to `mozilla::Unused`}} + RefPtr kfdg_m7(p); + kfdg_m7.mPtr->f(nullptr, nullptr); + RefPtr kfdg_m8 = p; + kfdg_m8.mPtr->f(nullptr, nullptr); + + RefPtr kfdg_a3((already_AddRefed())); + RefPtr kfdg_a4 = already_AddRefed(); + + RefPtr kfdg_p3(param); + RefPtr kfdg_p4 = param; + } + + Type *p; +}; + +void f(nsCOMPtr ignoredArgument, Type *param) { + nsCOMPtr never_referenced; + Type t; + // Type *p = nullptr; + nsCOMPtr kfdg_m1(t.p); // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m1', or explicitly pass 'kfdg_m1' to `mozilla::Unused`}} + nsCOMPtr kfdg_m2 = t.p; // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m2', or explicitly pass 'kfdg_m2' to `mozilla::Unused`}} + nsCOMPtr kfdg_m3(t.p); + kfdg_m3.mPtr->f(nullptr, nullptr); + nsCOMPtr kfdg_m4 = t.p; + kfdg_m4.mPtr->f(nullptr, nullptr); + + nsCOMPtr kfdg_a1((already_AddRefed())); + nsCOMPtr kfdg_a2 = already_AddRefed(); + + nsCOMPtr kfdg_p1(param); + nsCOMPtr kfdg_p2 = param; + + + RefPtr never_referenced2; + RefPtr kfdg_m5(t.p); // expected-error {{Unused "kungFuDeathGrip" 'RefPtr' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m5', or explicitly pass 'kfdg_m5' to `mozilla::Unused`}} + RefPtr kfdg_m6 = t.p; // expected-error {{Unused "kungFuDeathGrip" 'RefPtr' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m6', or explicitly pass 'kfdg_m6' to `mozilla::Unused`}} + RefPtr kfdg_m7(t.p); + kfdg_m7.mPtr->f(nullptr, nullptr); + RefPtr kfdg_m8 = t.p; + kfdg_m8.mPtr->f(nullptr, nullptr); + + RefPtr kfdg_a3((already_AddRefed())); + RefPtr kfdg_a4 = already_AddRefed(); + + RefPtr kfdg_p3(param); + RefPtr kfdg_p4 = param; +} + +nsCOMPtr Type::someStaticCOMPtr(nullptr); diff --git a/build/clang-plugin/tests/TestMultipleAnnotations.cpp b/build/clang-plugin/tests/TestMultipleAnnotations.cpp new file mode 100644 index 000000000..aa927259d --- /dev/null +++ b/build/clang-plugin/tests/TestMultipleAnnotations.cpp @@ -0,0 +1,17 @@ +#define MOZ_MUST_USE_TYPE __attribute__((annotate("moz_must_use_type"))) +#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) + +class MOZ_MUST_USE_TYPE MOZ_STACK_CLASS TestClass {}; + +TestClass foo; // expected-error {{variable of type 'TestClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} + +TestClass f() +{ + TestClass bar; + return bar; +} + +void g() +{ + f(); // expected-error {{Unused value of must-use type 'TestClass'}} +} diff --git a/build/clang-plugin/tests/TestMustOverride.cpp b/build/clang-plugin/tests/TestMustOverride.cpp new file mode 100644 index 000000000..8e053f6c2 --- /dev/null +++ b/build/clang-plugin/tests/TestMustOverride.cpp @@ -0,0 +1,63 @@ +#define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override"))) +// Ignore warnings not related to static analysis here +#pragma GCC diagnostic ignored "-Woverloaded-virtual" + +struct S { + virtual void f() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + virtual void g() MOZ_MUST_OVERRIDE; + virtual void h() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} +}; +struct C : S { // expected-error {{'C' must override 'f'}} expected-error {{'C' must override 'h'}} + virtual void g() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + virtual void h(int); + void q() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} +}; +struct D : C { // expected-error {{'D' must override 'g'}} expected-error {{'D' must override 'q'}} + virtual void f(); +}; + +struct Base { + virtual void VirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + void NonVirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + static void StaticMethod() MOZ_MUST_OVERRIDE; +}; + +struct DoesNotPropagate : Base { + virtual void VirtMethod(); + void NonVirtMethod(); + static void StaticMethod(); +}; + +struct Final : DoesNotPropagate { }; + +struct Propagates : Base { + virtual void VirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + void NonVirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + static void StaticMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} +}; + +struct FailsFinal : Propagates { }; // expected-error {{'FailsFinal' must override 'VirtMethod'}} expected-error {{'FailsFinal' must override 'NonVirtMethod'}} expected-error {{'FailsFinal' must override 'StaticMethod'}} + +struct WrongOverload : Base { // expected-error {{'WrongOverload' must override 'VirtMethod'}} expected-error {{'WrongOverload' must override 'NonVirtMethod'}} + virtual void VirtMethod() const; + void NonVirtMethod(int param); + static void StaticMethod(); +}; + +namespace A { namespace B { namespace C { + struct Param {}; + struct Base { + void f(Param p) MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + }; +}}} + +struct Param {}; + +struct Derived : A::B::C::Base { + typedef A::B::C::Param Typedef; + void f(Typedef t); +}; + +struct BadDerived : A::B::C::Base { // expected-error {{'BadDerived' must override 'f'}} + void f(Param p); +}; diff --git a/build/clang-plugin/tests/TestMustUse.cpp b/build/clang-plugin/tests/TestMustUse.cpp new file mode 100644 index 000000000..7878a4cde --- /dev/null +++ b/build/clang-plugin/tests/TestMustUse.cpp @@ -0,0 +1,201 @@ +#define MOZ_MUST_USE_TYPE __attribute__((annotate("moz_must_use_type"))) + +struct Temporary { ~Temporary(); }; +class MOZ_MUST_USE_TYPE MustUse {}; +class MayUse {}; + +MustUse producesMustUse(); +MustUse *producesMustUsePointer(); +MustUse &producesMustUseRef(); + +MustUse producesMustUse(const Temporary& t); +MustUse *producesMustUsePointer(const Temporary& t); +MustUse &producesMustUseRef(const Temporary& t); + +MayUse producesMayUse(); +MayUse *producesMayUsePointer(); +MayUse &producesMayUseRef(); + +void use(MustUse*); +void use(MustUse&); +void use(MustUse&&); +void use(MayUse*); +void use(MayUse&); +void use(MayUse&&); +void use(bool); + +void foo() { + MustUse u; + + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + { + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + } + if (true) { + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + } else { + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + } + + if(true) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + else producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + if(true) producesMustUsePointer(); + else producesMustUsePointer(); + if(true) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + else producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + if(true) producesMayUse(); + else producesMayUse(); + if(true) producesMayUsePointer(); + else producesMayUsePointer(); + if(true) producesMayUseRef(); + else producesMayUseRef(); + if(true) u = producesMustUse(); + else u = producesMustUse(); + if(true) u = producesMustUse(Temporary()); + else u = producesMustUse(Temporary()); + + while (true) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + while (true) producesMustUsePointer(); + while (true) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + while (true) producesMayUse(); + while (true) producesMayUsePointer(); + while (true) producesMayUseRef(); + while (true) u = producesMustUse(); + while (true) u = producesMustUse(Temporary()); + + do producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + while (true); + do producesMustUsePointer(); + while (true); + do producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + while (true); + do producesMayUse(); + while (true); + do producesMayUsePointer(); + while (true); + do producesMayUseRef(); + while (true); + do u = producesMustUse(); + while (true); + do u = producesMustUse(Temporary()); + while (true); + + for (;;) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + for (;;) producesMustUsePointer(); + for (;;) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + for (;;) producesMayUse(); + for (;;) producesMayUsePointer(); + for (;;) producesMayUseRef(); + for (;;) u = producesMustUse(); + for (;;) u = producesMustUse(Temporary()); + + for (producesMustUse();;); // expected-error {{Unused value of must-use type 'MustUse'}} + for (producesMustUsePointer();;); + for (producesMustUseRef();;); // expected-error {{Unused value of must-use type 'MustUse'}} + for (producesMayUse();;); + for (producesMayUsePointer();;); + for (producesMayUseRef();;); + for (u = producesMustUse();;); + for (u = producesMustUse(Temporary());;); + + for (;;producesMustUse()); // expected-error {{Unused value of must-use type 'MustUse'}} + for (;;producesMustUsePointer()); + for (;;producesMustUseRef()); // expected-error {{Unused value of must-use type 'MustUse'}} + for (;;producesMayUse()); + for (;;producesMayUsePointer()); + for (;;producesMayUseRef()); + for (;;u = producesMustUse()); + for (;;u = producesMustUse(Temporary())); + + use((producesMustUse(), false)); // expected-error {{Unused value of must-use type 'MustUse'}} + use((producesMustUsePointer(), false)); + use((producesMustUseRef(), false)); // expected-error {{Unused value of must-use type 'MustUse'}} + use((producesMayUse(), false)); + use((producesMayUsePointer(), false)); + use((producesMayUseRef(), false)); + use((u = producesMustUse(), false)); + use((u = producesMustUse(Temporary()), false)); + + switch (1) { + case 1: + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + case 2: + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + case 3: + producesMustUsePointer(); + case 4: + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + case 5: + producesMayUse(); + case 6: + producesMayUsePointer(); + case 7: + producesMayUseRef(); + case 8: + u = producesMustUse(); + case 9: + u = producesMustUse(Temporary()); + default: + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + } + + use(producesMustUse()); + use(producesMustUsePointer()); + use(producesMustUseRef()); + use(producesMayUse()); + use(producesMayUsePointer()); + use(producesMayUseRef()); + use(u = producesMustUse()); + use(u = producesMustUse(Temporary())); + + MustUse a = producesMustUse(); + MustUse *b = producesMustUsePointer(); + MustUse &c = producesMustUseRef(); + MayUse d = producesMayUse(); + MayUse *e = producesMayUsePointer(); + MayUse &f = producesMayUseRef(); + MustUse g = u = producesMustUse(); + MustUse h = u = producesMustUse(Temporary()); +} diff --git a/build/clang-plugin/tests/TestNANTestingExpr.cpp b/build/clang-plugin/tests/TestNANTestingExpr.cpp new file mode 100644 index 000000000..943577d4a --- /dev/null +++ b/build/clang-plugin/tests/TestNANTestingExpr.cpp @@ -0,0 +1,16 @@ +void test(bool x); +void foo() { + float f, f2; + typedef double mydouble; + mydouble d; + double d2; + test(f == f); // expected-error{{comparing a floating point value to itself for NaN checking can lead to incorrect results}} expected-note{{consider using mozilla::IsNaN instead}} + test(d == d); // expected-error{{comparing a floating point value to itself for NaN checking can lead to incorrect results}} expected-note{{consider using mozilla::IsNaN instead}} + test(f != f); // expected-error{{comparing a floating point value to itself for NaN checking can lead to incorrect results}} expected-note{{consider using mozilla::IsNaN instead}} + test(d != d); // expected-error{{comparing a floating point value to itself for NaN checking can lead to incorrect results}} expected-note{{consider using mozilla::IsNaN instead}} + test(f != d); + test(d == (d - f)); + test(f == f2); + test(d == d2); + test(d + 1 == d); +} diff --git a/build/clang-plugin/tests/TestNANTestingExprC.c b/build/clang-plugin/tests/TestNANTestingExprC.c new file mode 100644 index 000000000..ab2fead22 --- /dev/null +++ b/build/clang-plugin/tests/TestNANTestingExprC.c @@ -0,0 +1,17 @@ +/* expected-no-diagnostics */ +void test(int x); +void foo() { + float f, f2; + typedef double mydouble; + mydouble d; + double d2; + test(f == f); + test(d == d); + test(f != f); + test(d != d); + test(f != d); + test(d == (d - f)); + test(f == f2); + test(d == d2); + test(d + 1 == d); +} diff --git a/build/clang-plugin/tests/TestNeedsNoVTableType.cpp b/build/clang-plugin/tests/TestNeedsNoVTableType.cpp new file mode 100644 index 000000000..531a1c82a --- /dev/null +++ b/build/clang-plugin/tests/TestNeedsNoVTableType.cpp @@ -0,0 +1,94 @@ +#define MOZ_NEEDS_NO_VTABLE_TYPE __attribute__((annotate("moz_needs_no_vtable_type"))) + +template +struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer { // expected-error {{'PickyConsumer' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer' cannot be instantiated because 'G' has a VTable}} + T *m; +}; + +template +struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer_A { // expected-error {{'PickyConsumer_A' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer_A' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer_A' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer_A' cannot be instantiated because 'G' has a VTable}} + T *m; +}; +template +struct PickyConsumerWrapper { + PickyConsumer_A m; // expected-note {{bad instantiation of 'PickyConsumer_A' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A' requested here}} +}; + +template +struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer_B { // expected-error {{'PickyConsumer_B' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer_B' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer_B' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer_B' cannot be instantiated because 'G' has a VTable}} + T *m; +}; +template +struct PickyConsumerSubclass : PickyConsumer_B {}; // expected-note {{bad instantiation of 'PickyConsumer_B' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B' requested here}} + +template +struct NonPickyConsumer { + T *m; +}; + +struct A {}; +struct B : virtual A {}; +struct C : A {}; +struct D { + void d(); +}; +struct E { + virtual void e(); +}; +struct F : E { + virtual void e() final; +}; +struct G { + virtual void e() = 0; +}; + +void f() { + { + PickyConsumer a1; + PickyConsumerWrapper a2; + PickyConsumerSubclass a3; + NonPickyConsumer a4; + } + + { + PickyConsumer a1; // expected-note {{bad instantiation of 'PickyConsumer' requested here}} + PickyConsumerWrapper a2; + PickyConsumerSubclass a3; + NonPickyConsumer a4; + } + + { + PickyConsumer a1; + PickyConsumerWrapper a2; + PickyConsumerSubclass a3; + NonPickyConsumer a4; + } + + { + PickyConsumer a1; + PickyConsumerWrapper a2; + PickyConsumerSubclass a3; + NonPickyConsumer a4; + } + + { + PickyConsumer a1; // expected-note {{bad instantiation of 'PickyConsumer' requested here}} + PickyConsumerWrapper a2; + PickyConsumerSubclass a3; + NonPickyConsumer a4; + } + + { + PickyConsumer a1; // expected-note {{bad instantiation of 'PickyConsumer' requested here}} + PickyConsumerWrapper a2; + PickyConsumerSubclass a3; + NonPickyConsumer a4; + } + + { + PickyConsumer a1; // expected-note {{bad instantiation of 'PickyConsumer' requested here}} + PickyConsumerWrapper a2; + PickyConsumerSubclass a3; + NonPickyConsumer a4; + } +} diff --git a/build/clang-plugin/tests/TestNoAddRefReleaseOnReturn.cpp b/build/clang-plugin/tests/TestNoAddRefReleaseOnReturn.cpp new file mode 100644 index 000000000..2e1f83377 --- /dev/null +++ b/build/clang-plugin/tests/TestNoAddRefReleaseOnReturn.cpp @@ -0,0 +1,110 @@ +#define MOZ_NO_ADDREF_RELEASE_ON_RETURN __attribute__((annotate("moz_no_addref_release_on_return"))) + +struct Test { + void AddRef(); + void Release(); + void foo(); +}; + +struct TestD : Test {}; + +struct S { + Test* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + Test& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + Test h() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +}; + +struct SD { + TestD* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + TestD& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + TestD h() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +}; + +template +struct X { + T* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + T& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + T h() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +}; + +template +struct SP { + T* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +}; + +Test* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +Test& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +Test h() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + +TestD* fd() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +TestD& gd() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +TestD hd() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + +void test() { + S s; + s.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}} + s.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}} + s.f()->foo(); + s.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}} + s.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}} + s.g().foo(); + s.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}} + s.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}} + s.h().foo(); + SD sd; + sd.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}} + sd.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}} + sd.f()->foo(); + sd.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}} + sd.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}} + sd.g().foo(); + sd.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}} + sd.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}} + sd.h().foo(); + X x; + x.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}} + x.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}} + x.f()->foo(); + x.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}} + x.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}} + x.g().foo(); + x.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}} + x.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}} + x.h().foo(); + X xd; + xd.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}} + xd.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}} + xd.f()->foo(); + xd.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}} + xd.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}} + xd.g().foo(); + xd.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}} + xd.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}} + xd.h().foo(); + SP sp; + sp->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'operator->'}} + sp->Release(); // expected-error{{'Release' cannot be called on the return value of 'operator->'}} + sp->foo(); + SP spd; + spd->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'operator->'}} + spd->Release(); // expected-error{{'Release' cannot be called on the return value of 'operator->'}} + spd->foo(); + f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}} + f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}} + f()->foo(); + g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}} + g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}} + g().foo(); + h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}} + h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}} + h().foo(); + fd()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'fd'}} + fd()->Release(); // expected-error{{'Release' cannot be called on the return value of 'fd'}} + fd()->foo(); + gd().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'gd'}} + gd().Release(); // expected-error{{'Release' cannot be called on the return value of 'gd'}} + gd().foo(); + hd().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'hd'}} + hd().Release(); // expected-error{{'Release' cannot be called on the return value of 'hd'}} + hd().foo(); +} diff --git a/build/clang-plugin/tests/TestNoArithmeticExprInArgument.cpp b/build/clang-plugin/tests/TestNoArithmeticExprInArgument.cpp new file mode 100644 index 000000000..d147b1701 --- /dev/null +++ b/build/clang-plugin/tests/TestNoArithmeticExprInArgument.cpp @@ -0,0 +1,32 @@ +#define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT __attribute__((annotate("moz_no_arith_expr_in_arg"))) + +struct X { + explicit X(int) MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT; + void baz(int) MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT; +}; + +int operator+(int, X); +int operator+(X, int); +int operator++(X); + +void badArithmeticsInArgs() { + int a = 1; + typedef int myint; + myint b = 2; + X goodObj1(a); + goodObj1.baz(b); + X badObj1(a + b); // expected-error{{cannot pass an arithmetic expression of built-in types to 'X'}} + X badObj2 = X(a ? 0 : ++a); // expected-error{{cannot pass an arithmetic expression of built-in types to 'X'}} + X badObj3(~a); // expected-error{{cannot pass an arithmetic expression of built-in types to 'X'}} + badObj1.baz(a - 1 - b); // expected-error{{cannot pass an arithmetic expression of built-in types to 'baz'}} + badObj1.baz(++a); // expected-error{{cannot pass an arithmetic expression of built-in types to 'baz'}} + badObj1.baz(a++); // expected-error{{cannot pass an arithmetic expression of built-in types to 'baz'}} + badObj1.baz(a || b); + badObj1.baz(a + goodObj1); + badObj1.baz(goodObj1 + a); + badObj1.baz(++goodObj1); + badObj1.baz(-1); + badObj1.baz(-1.0); + badObj1.baz(1 + 2); + badObj1.baz(1 << (sizeof(int)/2)); +} diff --git a/build/clang-plugin/tests/TestNoAutoType.cpp b/build/clang-plugin/tests/TestNoAutoType.cpp new file mode 100644 index 000000000..6c6e65f24 --- /dev/null +++ b/build/clang-plugin/tests/TestNoAutoType.cpp @@ -0,0 +1,41 @@ +#define MOZ_NON_AUTOABLE __attribute__((annotate("moz_non_autoable"))) + +template +struct MOZ_NON_AUTOABLE ExplicitTypeTemplate {}; +struct MOZ_NON_AUTOABLE ExplicitType {}; +struct NonExplicitType {}; + +void f() { + { + ExplicitType a; + auto b = a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitType'}} expected-note {{Please write out this type explicitly}} + auto &br = a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitType &'}} expected-note {{Please write out this type explicitly}} + const auto &brc = a; // expected-error {{Cannot use auto to declare a variable of type 'const ExplicitType &'}} expected-note {{Please write out this type explicitly}} + auto *bp = &a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitType *'}} expected-note {{Please write out this type explicitly}} + const auto *bpc = &a; // expected-error {{Cannot use auto to declare a variable of type 'const ExplicitType *'}} expected-note {{Please write out this type explicitly}} + } + + { + ExplicitTypeTemplate a; + auto b = a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitTypeTemplate'}} expected-note {{Please write out this type explicitly}} + auto &br = a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitTypeTemplate &'}} expected-note {{Please write out this type explicitly}} + const auto &brc = a; // expected-error {{Cannot use auto to declare a variable of type 'const ExplicitTypeTemplate &'}} expected-note {{Please write out this type explicitly}} + auto *bp = &a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitTypeTemplate *'}} expected-note {{Please write out this type explicitly}} + const auto *bpc = &a; // expected-error {{Cannot use auto to declare a variable of type 'const ExplicitTypeTemplate *'}} expected-note {{Please write out this type explicitly}} + } + + { + NonExplicitType c; + auto d = c; + auto &dr = c; + const auto &drc = c; + auto *dp = &c; + const auto *dpc = &c; + } +} + +ExplicitType A; +auto B = A; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitType'}} expected-note {{Please write out this type explicitly}} + +NonExplicitType C; +auto D = C; diff --git a/build/clang-plugin/tests/TestNoDuplicateRefCntMember.cpp b/build/clang-plugin/tests/TestNoDuplicateRefCntMember.cpp new file mode 100644 index 000000000..ff68e4fc7 --- /dev/null +++ b/build/clang-plugin/tests/TestNoDuplicateRefCntMember.cpp @@ -0,0 +1,49 @@ +class C1 {}; + +class RC1 { +public: + virtual void AddRef(); + virtual void Release(); + +private: + int mRefCnt; // expected-note 2 {{Superclass 'RC1' also has an mRefCnt member}} expected-note 3 {{Superclass 'RC1' has an mRefCnt member}} +}; + +class RC2 : public RC1 { // expected-error {{Refcounted record 'RC2' has multiple mRefCnt members}} +public: + virtual void AddRef(); + virtual void Release(); + +private: + int mRefCnt; // expected-note {{Consider using the _INHERITED macros for AddRef and Release here}} +}; + +class C2 : public RC1 {}; + +class RC3 : public RC1 {}; + +class RC4 : public RC3, public C2 {}; // expected-error {{Refcounted record 'RC4' has multiple superclasses with mRefCnt members}} + +class RC5 : public RC1 {}; + +class RC6 : public C1, public RC5 { // expected-error {{Refcounted record 'RC6' has multiple mRefCnt members}} +public: + virtual void AddRef(); + virtual void Release(); + +private: + int mRefCnt; // expected-note {{Consider using the _INHERITED macros for AddRef and Release here}} +}; + +class Predecl; + +class OtherRC { +public: + virtual void AddRef(); + virtual void Release(); + +private: + int mRefCnt; // expected-note {{Superclass 'OtherRC' has an mRefCnt member}} +}; + +class MultRCSuper : public RC1, public OtherRC {}; // expected-error {{Refcounted record 'MultRCSuper' has multiple superclasses with mRefCnt members}} diff --git a/build/clang-plugin/tests/TestNoExplicitMoveConstructor.cpp b/build/clang-plugin/tests/TestNoExplicitMoveConstructor.cpp new file mode 100644 index 000000000..5aea6b1a7 --- /dev/null +++ b/build/clang-plugin/tests/TestNoExplicitMoveConstructor.cpp @@ -0,0 +1,25 @@ +class Foo { + Foo(Foo&& f); +}; + +class Bar { + explicit Bar(Bar&& f); // expected-error {{Move constructors may not be marked explicit}} +}; + +class Baz { + template + explicit Baz(T&& f) {}; +}; + +class Quxx { + Quxx(); + Quxx(Quxx& q) = delete; + template + explicit Quxx(T&& f) {}; +}; + +void f() { + // Move a quxx into a quxx! (This speciailizes Quxx's constructor to look like + // a move constructor - to make sure it doesn't trigger) + Quxx(Quxx()); +} diff --git a/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp b/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp new file mode 100644 index 000000000..2c74a34ba --- /dev/null +++ b/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp @@ -0,0 +1,651 @@ +#include +#include "mozilla/Function.h" +#define MOZ_STRONG_REF __attribute__((annotate("moz_strong_ref"))) + +struct RefCountedBase { + void AddRef(); + void Release(); +}; + +template +struct SmartPtr { + T* MOZ_STRONG_REF t; + T* operator->() const; +}; + +struct R : RefCountedBase { + void method(); +}; + +void take(...); +void foo() { + R* ptr; + SmartPtr sp; + take([&](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + take([&](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + take([&](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + take([&](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + take([=](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + take([=](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + take([=](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + take([=](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + take([ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + take([sp](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + take([ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + take([sp](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + take([&ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + take([&sp](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + take([&ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + take([&sp](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); +} + +void b() { + R* ptr; + SmartPtr sp; + std::function([&](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + std::function)>([&](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + std::function([&](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + std::function)>([&](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + std::function([=](R* argptr) { + R* localptr; + ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + argptr->method(); + localptr->method(); + }); + std::function)>([=](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + std::function([=](R* argptr) { + R* localptr; + take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + take(argptr); + take(localptr); + }); + std::function)>([=](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + std::function([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + std::function)>([sp](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + std::function([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + std::function)>([sp](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + std::function([&ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + std::function)>([&sp](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + std::function([&ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + std::function)>([&sp](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); +} + +void c() { + R* ptr; + SmartPtr sp; + mozilla::function([&](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + mozilla::function)>([&](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + mozilla::function([&](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + mozilla::function)>([&](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + mozilla::function([=](R* argptr) { + R* localptr; + ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + argptr->method(); + localptr->method(); + }); + mozilla::function)>([=](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + mozilla::function([=](R* argptr) { + R* localptr; + take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + take(argptr); + take(localptr); + }); + mozilla::function)>([=](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + mozilla::function([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + mozilla::function)>([sp](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + mozilla::function([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + mozilla::function)>([sp](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + mozilla::function([&ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + mozilla::function)>([&sp](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + mozilla::function([&ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + mozilla::function)>([&sp](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); +} + +// These tests would check c++14 deduced return types, if they were supported in +// our codebase. They are being kept here for convenience in the future if we do +// add support for c++14 deduced return types +#if 0 +auto d1() { + R* ptr; + SmartPtr sp; + return ([&](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); +} +auto d2() { + R* ptr; + SmartPtr sp; + return ([&](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); +} +auto d3() { + R* ptr; + SmartPtr sp; + return ([&](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); +} +auto d4() { + R* ptr; + SmartPtr sp; + return ([&](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); +} +auto d5() { + R* ptr; + SmartPtr sp; + return ([=](R* argptr) { + R* localptr; + ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + argptr->method(); + localptr->method(); + }); +} +auto d6() { + R* ptr; + SmartPtr sp; + return ([=](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); +} +auto d8() { + R* ptr; + SmartPtr sp; + return ([=](R* argptr) { + R* localptr; + take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + take(argptr); + take(localptr); + }); +} +auto d9() { + R* ptr; + SmartPtr sp; + return ([=](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); +} +auto d10() { + R* ptr; + SmartPtr sp; + return ([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); +} +auto d11() { + R* ptr; + SmartPtr sp; + return ([sp](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); +} +auto d12() { + R* ptr; + SmartPtr sp; + return ([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); +} +auto d13() { + R* ptr; + SmartPtr sp; + return ([sp](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); +} +auto d14() { + R* ptr; + SmartPtr sp; + return ([&ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); +} +auto d15() { + R* ptr; + SmartPtr sp; + return ([&sp](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); +} +auto d16() { + R* ptr; + SmartPtr sp; + return ([&ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); +} +auto d17() { + R* ptr; + SmartPtr sp; + return ([&sp](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); +} +#endif + +void e() { + auto e1 = []() { + R* ptr; + SmartPtr sp; + return ([&](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + }; + auto e2 = []() { + R* ptr; + SmartPtr sp; + return ([&](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + }; + auto e3 = []() { + R* ptr; + SmartPtr sp; + return ([&](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + }; + auto e4 = []() { + R* ptr; + SmartPtr sp; + return ([&](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + }; + auto e5 = []() { + R* ptr; + SmartPtr sp; + return ([=](R* argptr) { + R* localptr; + ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + argptr->method(); + localptr->method(); + }); + }; + auto e6 = []() { + R* ptr; + SmartPtr sp; + return ([=](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + }; + auto e8 = []() { + R* ptr; + SmartPtr sp; + return ([=](R* argptr) { + R* localptr; + take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + take(argptr); + take(localptr); + }); + }; + auto e9 = []() { + R* ptr; + SmartPtr sp; + return ([=](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + }; + auto e10 = []() { + R* ptr; + SmartPtr sp; + return ([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + }; + auto e11 = []() { + R* ptr; + SmartPtr sp; + return ([sp](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + }; + auto e12 = []() { + R* ptr; + SmartPtr sp; + return ([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + }; + auto e13 = []() { + R* ptr; + SmartPtr sp; + return ([sp](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + }; + auto e14 = []() { + R* ptr; + SmartPtr sp; + return ([&ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + }; + auto e15 = []() { + R* ptr; + SmartPtr sp; + return ([&sp](SmartPtr argsp) { + SmartPtr localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + }; + auto e16 = []() { + R* ptr; + SmartPtr sp; + return ([&ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + }; + auto e17 = []() { + R* ptr; + SmartPtr sp; + return ([&sp](SmartPtr argsp) { + SmartPtr localsp; + take(sp); + take(argsp); + take(localsp); + }); + }; +} diff --git a/build/clang-plugin/tests/TestNonHeapClass.cpp b/build/clang-plugin/tests/TestNonHeapClass.cpp new file mode 100644 index 000000000..26fe6404e --- /dev/null +++ b/build/clang-plugin/tests/TestNonHeapClass.cpp @@ -0,0 +1,62 @@ +#define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class"))) +#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) +#include + +struct MOZ_NONHEAP_CLASS NonHeap { + int i; + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template +struct MOZ_NONHEAP_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void misuseNonHeapClass(int len) { + NonHeap valid; + NonHeap alsoValid[2]; + static NonHeap validStatic; + static NonHeap alsoValidStatic[2]; + + gobble(&valid); + gobble(&validStatic); + gobble(&alsoValid[0]); + + gobble(new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + gobble(new NonHeap[10]); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + gobble(new TemplateClass); // expected-error {{variable of type 'TemplateClass' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + gobble(len <= 5 ? &valid : new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + + char buffer[sizeof(NonHeap)]; + gobble(new (buffer) NonHeap); +} + +NonHeap validStatic; +struct RandomClass { + NonHeap nonstaticMember; // expected-note {{'RandomClass' is a non-heap type because member 'nonstaticMember' is a non-heap type 'NonHeap'}} + static NonHeap staticMember; +}; +struct MOZ_NONHEAP_CLASS RandomNonHeapClass { + NonHeap nonstaticMember; + static NonHeap staticMember; +}; + +struct BadInherit : NonHeap {}; // expected-note {{'BadInherit' is a non-heap type because it inherits from a non-heap type 'NonHeap'}} +struct MOZ_NONHEAP_CLASS GoodInherit : NonHeap {}; + +void useStuffWrongly() { + gobble(new BadInherit); // expected-error {{variable of type 'BadInherit' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + gobble(new RandomClass); // expected-error {{variable of type 'RandomClass' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} +} + +// Stack class overrides non-heap typees. +struct MOZ_STACK_CLASS StackClass {}; +struct MOZ_NONHEAP_CLASS InferredStackClass : GoodInherit { + NonHeap nonstaticMember; + StackClass stackClass; // expected-note {{'InferredStackClass' is a stack type because member 'stackClass' is a stack type 'StackClass'}} +}; + +InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} diff --git a/build/clang-plugin/tests/TestNonMemMovable.cpp b/build/clang-plugin/tests/TestNonMemMovable.cpp new file mode 100644 index 000000000..a726d0ab6 --- /dev/null +++ b/build/clang-plugin/tests/TestNonMemMovable.cpp @@ -0,0 +1,830 @@ +#define MOZ_NON_MEMMOVABLE __attribute__((annotate("moz_non_memmovable"))) +#define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type"))) +#define MOZ_NEEDS_MEMMOVABLE_MEMBERS __attribute__((annotate("moz_needs_memmovable_members"))) + +/* + These are a bunch of structs with variable levels of memmovability. + They will be used as template parameters to the various NeedyTemplates +*/ +struct MOZ_NON_MEMMOVABLE NonMovable {}; +struct Movable {}; + +// Subclasses +struct S_NonMovable : NonMovable {}; // expected-note 51 {{'S_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'NonMovable'}} +struct S_Movable : Movable {}; + +// Members +struct W_NonMovable { + NonMovable m; // expected-note 34 {{'W_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'NonMovable'}} +}; +struct W_Movable { + Movable m; +}; + +// Wrapped Subclasses +struct WS_NonMovable { + S_NonMovable m; // expected-note 34 {{'WS_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'S_NonMovable'}} +}; +struct WS_Movable { + S_Movable m; +}; + +// Combinations of the above +struct SW_NonMovable : W_NonMovable {}; // expected-note 17 {{'SW_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'W_NonMovable'}} +struct SW_Movable : W_Movable {}; + +struct SWS_NonMovable : WS_NonMovable {}; // expected-note 17 {{'SWS_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'WS_NonMovable'}} +struct SWS_Movable : WS_Movable {}; + +// Basic templated wrapper +template +struct Template_Inline { + T m; // expected-note-re 56 {{'Template_Inline<{{.*}}>' is a non-memmove()able type because member 'm' is a non-memmove()able type '{{.*}}'}} +}; + +template +struct Template_Ref { + T* m; +}; + +template +struct Template_Unused {}; + +template +struct MOZ_NON_MEMMOVABLE Template_NonMovable {}; + +/* + These tests take the following form: + DECLARATIONS => Declarations of the templates which are either marked with MOZ_NEEDS_MEMMOVABLE_TYPE + or which instantiate a MOZ_NEEDS_MEMMOVABLE_TYPE through some mechanism. + BAD N => Instantiations of the wrapper template with each of the non-memmovable types. + The prefix S_ means subclass, W_ means wrapped. Each of these rows should produce an error + on the NeedyTemplate in question, and a note at the instantiation location of that template. + Unfortunately, on every case more complicated than bad1, the instantiation location is + within another template. Thus, the notes are expected on the template in question which + actually instantiates the MOZ_NEEDS_MEMMOVABLE_TYPE template. + GOOD N => Instantiations of the wrapper template with each of the memmovable types. + This is meant as a sanity check to ensure that we don't reject valid instantiations of + templates. + + + Note 1: Each set uses it's own types to ensure that they don't re-use each-other's template specializations. + If they did, then some of the error messages would not be emitted (as error messages are emitted for template + specializations, rather than for variable declarations) + + Note 2: Every instance of NeedyTemplate contains a member of type T. This is to ensure that T is actually + instantiated (if T is a template) by clang. If T isn't instantiated, then we can't actually tell if it is + NON_MEMMOVABLE. (This is OK in practice, as you cannot memmove a type which you don't know the size of). + + Note 3: There are a set of tests for specializations of NeedyTemplate at the bottom. For each set of tests, + these tests contribute two expected errors to the templates. +*/ + +// +// 1 - Unwrapped MOZ_NEEDS_MEMMOVABLE_TYPE +// + +template +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate1 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate1<{{.*}}>' with non-memmovable template argument '{{.*}}'}} + +void bad1() { + NeedyTemplate1 a1; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 a2; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 a3; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 a4; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 a5; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 a6; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + + NeedyTemplate1 > b1; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > b2; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > b3; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > b4; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > b5; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > b6; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + + NeedyTemplate1 > c1; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c2; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c3; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c4; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c5; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c6; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c7; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c8; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c9; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c10; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c11; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1 > c12; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} +} + +void good1() { + NeedyTemplate1 a1; + NeedyTemplate1 a2; + NeedyTemplate1 a3; + NeedyTemplate1 a4; + NeedyTemplate1 a5; + NeedyTemplate1 a6; + + NeedyTemplate1 > b1; + NeedyTemplate1 > b2; + NeedyTemplate1 > b3; + NeedyTemplate1 > b4; + NeedyTemplate1 > b5; + NeedyTemplate1 > b6; + + NeedyTemplate1 > c1; + NeedyTemplate1 > c2; + NeedyTemplate1 > c3; + NeedyTemplate1 > c4; + NeedyTemplate1 > c5; + NeedyTemplate1 > c6; + NeedyTemplate1 > c7; + NeedyTemplate1 > c8; + NeedyTemplate1 > c9; + NeedyTemplate1 > c10; + NeedyTemplate1 > c11; + NeedyTemplate1 > c12; + + NeedyTemplate1 > d1; + NeedyTemplate1 > d2; + NeedyTemplate1 > d3; + NeedyTemplate1 > d4; + NeedyTemplate1 > d5; + NeedyTemplate1 > d6; + NeedyTemplate1 > d7; + NeedyTemplate1 > d8; + NeedyTemplate1 > d9; + NeedyTemplate1 > d10; + NeedyTemplate1 > d11; + NeedyTemplate1 > d12; +} + +// +// 2 - Subclassed MOZ_NEEDS_MEMMOVABLE_TYPE +// + +template +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate2 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate2<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template +struct S_NeedyTemplate2 : NeedyTemplate2 {}; // expected-note-re 26 {{instantiation of 'NeedyTemplate2<{{.*}}>' requested here}} + +void bad2() { + S_NeedyTemplate2 a1; + S_NeedyTemplate2 a2; + S_NeedyTemplate2 a3; + S_NeedyTemplate2 a4; + S_NeedyTemplate2 a5; + S_NeedyTemplate2 a6; + + S_NeedyTemplate2 > b1; + S_NeedyTemplate2 > b2; + S_NeedyTemplate2 > b3; + S_NeedyTemplate2 > b4; + S_NeedyTemplate2 > b5; + S_NeedyTemplate2 > b6; + + S_NeedyTemplate2 > c1; + S_NeedyTemplate2 > c2; + S_NeedyTemplate2 > c3; + S_NeedyTemplate2 > c4; + S_NeedyTemplate2 > c5; + S_NeedyTemplate2 > c6; + S_NeedyTemplate2 > c7; + S_NeedyTemplate2 > c8; + S_NeedyTemplate2 > c9; + S_NeedyTemplate2 > c10; + S_NeedyTemplate2 > c11; + S_NeedyTemplate2 > c12; +} + +void good2() { + S_NeedyTemplate2 a1; + S_NeedyTemplate2 a2; + S_NeedyTemplate2 a3; + S_NeedyTemplate2 a4; + S_NeedyTemplate2 a5; + S_NeedyTemplate2 a6; + + S_NeedyTemplate2 > b1; + S_NeedyTemplate2 > b2; + S_NeedyTemplate2 > b3; + S_NeedyTemplate2 > b4; + S_NeedyTemplate2 > b5; + S_NeedyTemplate2 > b6; + + S_NeedyTemplate2 > c1; + S_NeedyTemplate2 > c2; + S_NeedyTemplate2 > c3; + S_NeedyTemplate2 > c4; + S_NeedyTemplate2 > c5; + S_NeedyTemplate2 > c6; + S_NeedyTemplate2 > c7; + S_NeedyTemplate2 > c8; + S_NeedyTemplate2 > c9; + S_NeedyTemplate2 > c10; + S_NeedyTemplate2 > c11; + S_NeedyTemplate2 > c12; + + S_NeedyTemplate2 > d1; + S_NeedyTemplate2 > d2; + S_NeedyTemplate2 > d3; + S_NeedyTemplate2 > d4; + S_NeedyTemplate2 > d5; + S_NeedyTemplate2 > d6; + S_NeedyTemplate2 > d7; + S_NeedyTemplate2 > d8; + S_NeedyTemplate2 > d9; + S_NeedyTemplate2 > d10; + S_NeedyTemplate2 > d11; + S_NeedyTemplate2 > d12; +} + +// +// 3 - Wrapped MOZ_NEEDS_MEMMOVABLE_TYPE +// + +template +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate3 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate3<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template +struct W_NeedyTemplate3 { + NeedyTemplate3 m; // expected-note-re 26 {{instantiation of 'NeedyTemplate3<{{.*}}>' requested here}} +}; +void bad3() { + W_NeedyTemplate3 a1; + W_NeedyTemplate3 a2; + W_NeedyTemplate3 a3; + W_NeedyTemplate3 a4; + W_NeedyTemplate3 a5; + W_NeedyTemplate3 a6; + + W_NeedyTemplate3 > b1; + W_NeedyTemplate3 > b2; + W_NeedyTemplate3 > b3; + W_NeedyTemplate3 > b4; + W_NeedyTemplate3 > b5; + W_NeedyTemplate3 > b6; + + W_NeedyTemplate3 > c1; + W_NeedyTemplate3 > c2; + W_NeedyTemplate3 > c3; + W_NeedyTemplate3 > c4; + W_NeedyTemplate3 > c5; + W_NeedyTemplate3 > c6; + W_NeedyTemplate3 > c7; + W_NeedyTemplate3 > c8; + W_NeedyTemplate3 > c9; + W_NeedyTemplate3 > c10; + W_NeedyTemplate3 > c11; + W_NeedyTemplate3 > c12; +} + +void good3() { + W_NeedyTemplate3 a1; + W_NeedyTemplate3 a2; + W_NeedyTemplate3 a3; + W_NeedyTemplate3 a4; + W_NeedyTemplate3 a5; + W_NeedyTemplate3 a6; + + W_NeedyTemplate3 > b1; + W_NeedyTemplate3 > b2; + W_NeedyTemplate3 > b3; + W_NeedyTemplate3 > b4; + W_NeedyTemplate3 > b5; + W_NeedyTemplate3 > b6; + + W_NeedyTemplate3 > c1; + W_NeedyTemplate3 > c2; + W_NeedyTemplate3 > c3; + W_NeedyTemplate3 > c4; + W_NeedyTemplate3 > c5; + W_NeedyTemplate3 > c6; + W_NeedyTemplate3 > c7; + W_NeedyTemplate3 > c8; + W_NeedyTemplate3 > c9; + W_NeedyTemplate3 > c10; + W_NeedyTemplate3 > c11; + W_NeedyTemplate3 > c12; + + W_NeedyTemplate3 > d1; + W_NeedyTemplate3 > d2; + W_NeedyTemplate3 > d3; + W_NeedyTemplate3 > d4; + W_NeedyTemplate3 > d5; + W_NeedyTemplate3 > d6; + W_NeedyTemplate3 > d7; + W_NeedyTemplate3 > d8; + W_NeedyTemplate3 > d9; + W_NeedyTemplate3 > d10; + W_NeedyTemplate3 > d11; + W_NeedyTemplate3 > d12; +} + +// +// 4 - Wrapped Subclassed MOZ_NEEDS_MEMMOVABLE_TYPE +// + +template +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate4 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate4<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template +struct S_NeedyTemplate4 : NeedyTemplate4 {}; // expected-note-re 26 {{instantiation of 'NeedyTemplate4<{{.*}}>' requested here}} +template +struct WS_NeedyTemplate4 { + S_NeedyTemplate4 m; +}; +void bad4() { + WS_NeedyTemplate4 a1; + WS_NeedyTemplate4 a2; + WS_NeedyTemplate4 a3; + WS_NeedyTemplate4 a4; + WS_NeedyTemplate4 a5; + WS_NeedyTemplate4 a6; + + WS_NeedyTemplate4 > b1; + WS_NeedyTemplate4 > b2; + WS_NeedyTemplate4 > b3; + WS_NeedyTemplate4 > b4; + WS_NeedyTemplate4 > b5; + WS_NeedyTemplate4 > b6; + + WS_NeedyTemplate4 > c1; + WS_NeedyTemplate4 > c2; + WS_NeedyTemplate4 > c3; + WS_NeedyTemplate4 > c4; + WS_NeedyTemplate4 > c5; + WS_NeedyTemplate4 > c6; + WS_NeedyTemplate4 > c7; + WS_NeedyTemplate4 > c8; + WS_NeedyTemplate4 > c9; + WS_NeedyTemplate4 > c10; + WS_NeedyTemplate4 > c11; + WS_NeedyTemplate4 > c12; +} + +void good4() { + WS_NeedyTemplate4 a1; + WS_NeedyTemplate4 a2; + WS_NeedyTemplate4 a3; + WS_NeedyTemplate4 a4; + WS_NeedyTemplate4 a5; + WS_NeedyTemplate4 a6; + + WS_NeedyTemplate4 > b1; + WS_NeedyTemplate4 > b2; + WS_NeedyTemplate4 > b3; + WS_NeedyTemplate4 > b4; + WS_NeedyTemplate4 > b5; + WS_NeedyTemplate4 > b6; + + WS_NeedyTemplate4 > c1; + WS_NeedyTemplate4 > c2; + WS_NeedyTemplate4 > c3; + WS_NeedyTemplate4 > c4; + WS_NeedyTemplate4 > c5; + WS_NeedyTemplate4 > c6; + WS_NeedyTemplate4 > c7; + WS_NeedyTemplate4 > c8; + WS_NeedyTemplate4 > c9; + WS_NeedyTemplate4 > c10; + WS_NeedyTemplate4 > c11; + WS_NeedyTemplate4 > c12; + + WS_NeedyTemplate4 > d1; + WS_NeedyTemplate4 > d2; + WS_NeedyTemplate4 > d3; + WS_NeedyTemplate4 > d4; + WS_NeedyTemplate4 > d5; + WS_NeedyTemplate4 > d6; + WS_NeedyTemplate4 > d7; + WS_NeedyTemplate4 > d8; + WS_NeedyTemplate4 > d9; + WS_NeedyTemplate4 > d10; + WS_NeedyTemplate4 > d11; + WS_NeedyTemplate4 > d12; +} + +// +// 5 - Subclassed Wrapped MOZ_NEEDS_MEMMOVABLE_TYPE +// + +template +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate5 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate5<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template +struct W_NeedyTemplate5 { + NeedyTemplate5 m; // expected-note-re 26 {{instantiation of 'NeedyTemplate5<{{.*}}>' requested here}} +}; +template +struct SW_NeedyTemplate5 : W_NeedyTemplate5 {}; +void bad5() { + SW_NeedyTemplate5 a1; + SW_NeedyTemplate5 a2; + SW_NeedyTemplate5 a3; + SW_NeedyTemplate5 a4; + SW_NeedyTemplate5 a5; + SW_NeedyTemplate5 a6; + + SW_NeedyTemplate5 > b1; + SW_NeedyTemplate5 > b2; + SW_NeedyTemplate5 > b3; + SW_NeedyTemplate5 > b4; + SW_NeedyTemplate5 > b5; + SW_NeedyTemplate5 > b6; + + SW_NeedyTemplate5 > c1; + SW_NeedyTemplate5 > c2; + SW_NeedyTemplate5 > c3; + SW_NeedyTemplate5 > c4; + SW_NeedyTemplate5 > c5; + SW_NeedyTemplate5 > c6; + SW_NeedyTemplate5 > c7; + SW_NeedyTemplate5 > c8; + SW_NeedyTemplate5 > c9; + SW_NeedyTemplate5 > c10; + SW_NeedyTemplate5 > c11; + SW_NeedyTemplate5 > c12; +} + +void good5() { + SW_NeedyTemplate5 a1; + SW_NeedyTemplate5 a2; + SW_NeedyTemplate5 a3; + SW_NeedyTemplate5 a4; + SW_NeedyTemplate5 a5; + SW_NeedyTemplate5 a6; + + SW_NeedyTemplate5 > b1; + SW_NeedyTemplate5 > b2; + SW_NeedyTemplate5 > b3; + SW_NeedyTemplate5 > b4; + SW_NeedyTemplate5 > b5; + SW_NeedyTemplate5 > b6; + + SW_NeedyTemplate5 > c1; + SW_NeedyTemplate5 > c2; + SW_NeedyTemplate5 > c3; + SW_NeedyTemplate5 > c4; + SW_NeedyTemplate5 > c5; + SW_NeedyTemplate5 > c6; + SW_NeedyTemplate5 > c7; + SW_NeedyTemplate5 > c8; + SW_NeedyTemplate5 > c9; + SW_NeedyTemplate5 > c10; + SW_NeedyTemplate5 > c11; + SW_NeedyTemplate5 > c12; + + SW_NeedyTemplate5 > d1; + SW_NeedyTemplate5 > d2; + SW_NeedyTemplate5 > d3; + SW_NeedyTemplate5 > d4; + SW_NeedyTemplate5 > d5; + SW_NeedyTemplate5 > d6; + SW_NeedyTemplate5 > d7; + SW_NeedyTemplate5 > d8; + SW_NeedyTemplate5 > d9; + SW_NeedyTemplate5 > d10; + SW_NeedyTemplate5 > d11; + SW_NeedyTemplate5 > d12; +} + +// +// 6 - MOZ_NEEDS_MEMMOVABLE_TYPE instantiated with default template argument +// +// Note: This has an extra error, because it also includes a test with the default template argument. +// + +template +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate6 {T m;}; // expected-error-re 27 {{Cannot instantiate 'NeedyTemplate6<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template +struct W_NeedyTemplate6 { + NeedyTemplate6 m; // expected-note-re 27 {{instantiation of 'NeedyTemplate6<{{.*}}>' requested here}} +}; +template +struct SW_NeedyTemplate6 : W_NeedyTemplate6 {}; +// We create a different NonMovable type here, as NeedyTemplate6 will already be instantiated with NonMovable +struct MOZ_NON_MEMMOVABLE NonMovable2 {}; +template +struct Defaulted_SW_NeedyTemplate6 { + SW_NeedyTemplate6 m; +}; +void bad6() { + Defaulted_SW_NeedyTemplate6 a1; + Defaulted_SW_NeedyTemplate6 a2; + Defaulted_SW_NeedyTemplate6 a3; + Defaulted_SW_NeedyTemplate6 a4; + Defaulted_SW_NeedyTemplate6 a5; + Defaulted_SW_NeedyTemplate6 a6; + + Defaulted_SW_NeedyTemplate6 > b1; + Defaulted_SW_NeedyTemplate6 > b2; + Defaulted_SW_NeedyTemplate6 > b3; + Defaulted_SW_NeedyTemplate6 > b4; + Defaulted_SW_NeedyTemplate6 > b5; + Defaulted_SW_NeedyTemplate6 > b6; + + Defaulted_SW_NeedyTemplate6 > c1; + Defaulted_SW_NeedyTemplate6 > c2; + Defaulted_SW_NeedyTemplate6 > c3; + Defaulted_SW_NeedyTemplate6 > c4; + Defaulted_SW_NeedyTemplate6 > c5; + Defaulted_SW_NeedyTemplate6 > c6; + Defaulted_SW_NeedyTemplate6 > c7; + Defaulted_SW_NeedyTemplate6 > c8; + Defaulted_SW_NeedyTemplate6 > c9; + Defaulted_SW_NeedyTemplate6 > c10; + Defaulted_SW_NeedyTemplate6 > c11; + Defaulted_SW_NeedyTemplate6 > c12; + + Defaulted_SW_NeedyTemplate6<> c13; +} + +void good6() { + Defaulted_SW_NeedyTemplate6 a1; + Defaulted_SW_NeedyTemplate6 a2; + Defaulted_SW_NeedyTemplate6 a3; + Defaulted_SW_NeedyTemplate6 a4; + Defaulted_SW_NeedyTemplate6 a5; + Defaulted_SW_NeedyTemplate6 a6; + + Defaulted_SW_NeedyTemplate6 > b1; + Defaulted_SW_NeedyTemplate6 > b2; + Defaulted_SW_NeedyTemplate6 > b3; + Defaulted_SW_NeedyTemplate6 > b4; + Defaulted_SW_NeedyTemplate6 > b5; + Defaulted_SW_NeedyTemplate6 > b6; + + Defaulted_SW_NeedyTemplate6 > c1; + Defaulted_SW_NeedyTemplate6 > c2; + Defaulted_SW_NeedyTemplate6 > c3; + Defaulted_SW_NeedyTemplate6 > c4; + Defaulted_SW_NeedyTemplate6 > c5; + Defaulted_SW_NeedyTemplate6 > c6; + Defaulted_SW_NeedyTemplate6 > c7; + Defaulted_SW_NeedyTemplate6 > c8; + Defaulted_SW_NeedyTemplate6 > c9; + Defaulted_SW_NeedyTemplate6 > c10; + Defaulted_SW_NeedyTemplate6 > c11; + Defaulted_SW_NeedyTemplate6 > c12; + + Defaulted_SW_NeedyTemplate6 > d1; + Defaulted_SW_NeedyTemplate6 > d2; + Defaulted_SW_NeedyTemplate6 > d3; + Defaulted_SW_NeedyTemplate6 > d4; + Defaulted_SW_NeedyTemplate6 > d5; + Defaulted_SW_NeedyTemplate6 > d6; + Defaulted_SW_NeedyTemplate6 > d7; + Defaulted_SW_NeedyTemplate6 > d8; + Defaulted_SW_NeedyTemplate6 > d9; + Defaulted_SW_NeedyTemplate6 > d10; + Defaulted_SW_NeedyTemplate6 > d11; + Defaulted_SW_NeedyTemplate6 > d12; +} + +// +// 7 - MOZ_NEEDS_MEMMOVABLE_TYPE instantiated as default template argument +// + +template +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate7 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate7<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template > +struct Defaulted_Templated_NeedyTemplate7 {Q m;}; // expected-note-re 26 {{instantiation of 'NeedyTemplate7<{{.*}}>' requested here}} +void bad7() { + Defaulted_Templated_NeedyTemplate7 a1; + Defaulted_Templated_NeedyTemplate7 a2; + Defaulted_Templated_NeedyTemplate7 a3; + Defaulted_Templated_NeedyTemplate7 a4; + Defaulted_Templated_NeedyTemplate7 a5; + Defaulted_Templated_NeedyTemplate7 a6; + + Defaulted_Templated_NeedyTemplate7 > b1; + Defaulted_Templated_NeedyTemplate7 > b2; + Defaulted_Templated_NeedyTemplate7 > b3; + Defaulted_Templated_NeedyTemplate7 > b4; + Defaulted_Templated_NeedyTemplate7 > b5; + Defaulted_Templated_NeedyTemplate7 > b6; + + Defaulted_Templated_NeedyTemplate7 > c1; + Defaulted_Templated_NeedyTemplate7 > c2; + Defaulted_Templated_NeedyTemplate7 > c3; + Defaulted_Templated_NeedyTemplate7 > c4; + Defaulted_Templated_NeedyTemplate7 > c5; + Defaulted_Templated_NeedyTemplate7 > c6; + Defaulted_Templated_NeedyTemplate7 > c7; + Defaulted_Templated_NeedyTemplate7 > c8; + Defaulted_Templated_NeedyTemplate7 > c9; + Defaulted_Templated_NeedyTemplate7 > c10; + Defaulted_Templated_NeedyTemplate7 > c11; + Defaulted_Templated_NeedyTemplate7 > c12; +} + +void good7() { + Defaulted_Templated_NeedyTemplate7 a1; + Defaulted_Templated_NeedyTemplate7 a2; + Defaulted_Templated_NeedyTemplate7 a3; + Defaulted_Templated_NeedyTemplate7 a4; + Defaulted_Templated_NeedyTemplate7 a5; + Defaulted_Templated_NeedyTemplate7 a6; + + Defaulted_Templated_NeedyTemplate7 > b1; + Defaulted_Templated_NeedyTemplate7 > b2; + Defaulted_Templated_NeedyTemplate7 > b3; + Defaulted_Templated_NeedyTemplate7 > b4; + Defaulted_Templated_NeedyTemplate7 > b5; + Defaulted_Templated_NeedyTemplate7 > b6; + + Defaulted_Templated_NeedyTemplate7 > c1; + Defaulted_Templated_NeedyTemplate7 > c2; + Defaulted_Templated_NeedyTemplate7 > c3; + Defaulted_Templated_NeedyTemplate7 > c4; + Defaulted_Templated_NeedyTemplate7 > c5; + Defaulted_Templated_NeedyTemplate7 > c6; + Defaulted_Templated_NeedyTemplate7 > c7; + Defaulted_Templated_NeedyTemplate7 > c8; + Defaulted_Templated_NeedyTemplate7 > c9; + Defaulted_Templated_NeedyTemplate7 > c10; + Defaulted_Templated_NeedyTemplate7 > c11; + Defaulted_Templated_NeedyTemplate7 > c12; + + Defaulted_Templated_NeedyTemplate7 > d1; + Defaulted_Templated_NeedyTemplate7 > d2; + Defaulted_Templated_NeedyTemplate7 > d3; + Defaulted_Templated_NeedyTemplate7 > d4; + Defaulted_Templated_NeedyTemplate7 > d5; + Defaulted_Templated_NeedyTemplate7 > d6; + Defaulted_Templated_NeedyTemplate7 > d7; + Defaulted_Templated_NeedyTemplate7 > d8; + Defaulted_Templated_NeedyTemplate7 > d9; + Defaulted_Templated_NeedyTemplate7 > d10; + Defaulted_Templated_NeedyTemplate7 > d11; + Defaulted_Templated_NeedyTemplate7 > d12; +} + +// +// 8 - Wrapped MOZ_NEEDS_MEMMOVABLE_TYPE instantiated as default template argument +// + +template +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate8 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate8<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template > +struct Defaulted_Templated_NeedyTemplate8 {Q m;}; // expected-note-re 26 {{instantiation of 'NeedyTemplate8<{{.*}}>' requested here}} +template +struct W_Defaulted_Templated_NeedyTemplate8 { + Defaulted_Templated_NeedyTemplate8 m; +}; +void bad8() { + W_Defaulted_Templated_NeedyTemplate8 a1; + W_Defaulted_Templated_NeedyTemplate8 a2; + W_Defaulted_Templated_NeedyTemplate8 a3; + W_Defaulted_Templated_NeedyTemplate8 a4; + W_Defaulted_Templated_NeedyTemplate8 a5; + W_Defaulted_Templated_NeedyTemplate8 a6; + + W_Defaulted_Templated_NeedyTemplate8 > b1; + W_Defaulted_Templated_NeedyTemplate8 > b2; + W_Defaulted_Templated_NeedyTemplate8 > b3; + W_Defaulted_Templated_NeedyTemplate8 > b4; + W_Defaulted_Templated_NeedyTemplate8 > b5; + W_Defaulted_Templated_NeedyTemplate8 > b6; + + W_Defaulted_Templated_NeedyTemplate8 > c1; + W_Defaulted_Templated_NeedyTemplate8 > c2; + W_Defaulted_Templated_NeedyTemplate8 > c3; + W_Defaulted_Templated_NeedyTemplate8 > c4; + W_Defaulted_Templated_NeedyTemplate8 > c5; + W_Defaulted_Templated_NeedyTemplate8 > c6; + W_Defaulted_Templated_NeedyTemplate8 > c7; + W_Defaulted_Templated_NeedyTemplate8 > c8; + W_Defaulted_Templated_NeedyTemplate8 > c9; + W_Defaulted_Templated_NeedyTemplate8 > c10; + W_Defaulted_Templated_NeedyTemplate8 > c11; + W_Defaulted_Templated_NeedyTemplate8 > c12; +} + +void good8() { + W_Defaulted_Templated_NeedyTemplate8 a1; + W_Defaulted_Templated_NeedyTemplate8 a2; + W_Defaulted_Templated_NeedyTemplate8 a3; + W_Defaulted_Templated_NeedyTemplate8 a4; + W_Defaulted_Templated_NeedyTemplate8 a5; + W_Defaulted_Templated_NeedyTemplate8 a6; + + W_Defaulted_Templated_NeedyTemplate8 > b1; + W_Defaulted_Templated_NeedyTemplate8 > b2; + W_Defaulted_Templated_NeedyTemplate8 > b3; + W_Defaulted_Templated_NeedyTemplate8 > b4; + W_Defaulted_Templated_NeedyTemplate8 > b5; + W_Defaulted_Templated_NeedyTemplate8 > b6; + + W_Defaulted_Templated_NeedyTemplate8 > c1; + W_Defaulted_Templated_NeedyTemplate8 > c2; + W_Defaulted_Templated_NeedyTemplate8 > c3; + W_Defaulted_Templated_NeedyTemplate8 > c4; + W_Defaulted_Templated_NeedyTemplate8 > c5; + W_Defaulted_Templated_NeedyTemplate8 > c6; + W_Defaulted_Templated_NeedyTemplate8 > c7; + W_Defaulted_Templated_NeedyTemplate8 > c8; + W_Defaulted_Templated_NeedyTemplate8 > c9; + W_Defaulted_Templated_NeedyTemplate8 > c10; + W_Defaulted_Templated_NeedyTemplate8 > c11; + W_Defaulted_Templated_NeedyTemplate8 > c12; + + W_Defaulted_Templated_NeedyTemplate8 > d1; + W_Defaulted_Templated_NeedyTemplate8 > d2; + W_Defaulted_Templated_NeedyTemplate8 > d3; + W_Defaulted_Templated_NeedyTemplate8 > d4; + W_Defaulted_Templated_NeedyTemplate8 > d5; + W_Defaulted_Templated_NeedyTemplate8 > d6; + W_Defaulted_Templated_NeedyTemplate8 > d7; + W_Defaulted_Templated_NeedyTemplate8 > d8; + W_Defaulted_Templated_NeedyTemplate8 > d9; + W_Defaulted_Templated_NeedyTemplate8 > d10; + W_Defaulted_Templated_NeedyTemplate8 > d11; + W_Defaulted_Templated_NeedyTemplate8 > d12; +} + +/* + SpecializedNonMovable is a non-movable class which has an explicit specialization of NeedyTemplate + for it. Instantiations of NeedyTemplateN should be legal as the explicit + specialization isn't annotated with MOZ_NEEDS_MEMMOVABLE_TYPE. + + However, as it is MOZ_NON_MEMMOVABLE, derived classes and members shouldn't be able to be used to + instantiate NeedyTemplate. +*/ + +struct MOZ_NON_MEMMOVABLE SpecializedNonMovable {}; +struct S_SpecializedNonMovable : SpecializedNonMovable {}; // expected-note 8 {{'S_SpecializedNonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'SpecializedNonMovable'}} + +// Specialize all of the NeedyTemplates with SpecializedNonMovable. +template <> +struct NeedyTemplate1 {}; +template <> +struct NeedyTemplate2 {}; +template <> +struct NeedyTemplate3 {}; +template <> +struct NeedyTemplate4 {}; +template <> +struct NeedyTemplate5 {}; +template <> +struct NeedyTemplate6 {}; +template <> +struct NeedyTemplate7 {}; +template <> +struct NeedyTemplate8 {}; + +void specialization() { + /* + SpecializedNonMovable has a specialization for every variant of NeedyTemplate, + so these templates are valid, even though SpecializedNonMovable isn't + memmovable + */ + NeedyTemplate1 a1; + S_NeedyTemplate2 a2; + W_NeedyTemplate3 a3; + WS_NeedyTemplate4 a4; + SW_NeedyTemplate5 a5; + Defaulted_SW_NeedyTemplate6 a6; + Defaulted_Templated_NeedyTemplate7 a7; + W_Defaulted_Templated_NeedyTemplate8 a8; + + /* + These entries contain an element which is SpecializedNonMovable, and are non-movable + as there is no valid specialization, and their member is non-memmovable + */ + NeedyTemplate1 > b1; // expected-note {{instantiation of 'NeedyTemplate1 >' requested here}} + S_NeedyTemplate2 > b2; + W_NeedyTemplate3 > b3; + WS_NeedyTemplate4 > b4; + SW_NeedyTemplate5 > b5; + Defaulted_SW_NeedyTemplate6 > b6; + Defaulted_Templated_NeedyTemplate7 > b7; + W_Defaulted_Templated_NeedyTemplate8 > b8; + + /* + The subclass of SpecializedNonMovable, is also non-memmovable, + as there is no valid specialization. + */ + NeedyTemplate1 c1; // expected-note {{instantiation of 'NeedyTemplate1' requested here}} + S_NeedyTemplate2 c2; + W_NeedyTemplate3 c3; + WS_NeedyTemplate4 c4; + SW_NeedyTemplate5 c5; + Defaulted_SW_NeedyTemplate6 c6; + Defaulted_Templated_NeedyTemplate7 c7; + W_Defaulted_Templated_NeedyTemplate8 c8; +} + +class MOZ_NEEDS_MEMMOVABLE_MEMBERS NeedsMemMovableMembers { + Movable m1; + NonMovable m2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'm2' of type 'NonMovable'}} + S_Movable sm1; + S_NonMovable sm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'sm2' of type 'S_NonMovable'}} + W_Movable wm1; + W_NonMovable wm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'wm2' of type 'W_NonMovable'}} + SW_Movable swm1; + SW_NonMovable swm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'swm2' of type 'SW_NonMovable'}} + WS_Movable wsm1; + WS_NonMovable wsm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'wsm2' of type 'WS_NonMovable'}} + SWS_Movable swsm1; + SWS_NonMovable swsm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'swsm2' of type 'SWS_NonMovable'}} +}; + +class NeedsMemMovableMembersDerived : public NeedsMemMovableMembers {}; diff --git a/build/clang-plugin/tests/TestNonMemMovableStd.cpp b/build/clang-plugin/tests/TestNonMemMovableStd.cpp new file mode 100644 index 000000000..9fce52496 --- /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/TestNonParameterChecker.cpp b/build/clang-plugin/tests/TestNonParameterChecker.cpp new file mode 100644 index 000000000..87ff89238 --- /dev/null +++ b/build/clang-plugin/tests/TestNonParameterChecker.cpp @@ -0,0 +1,179 @@ +#define MOZ_NON_PARAM __attribute__((annotate("moz_non_param"))) + +struct Param {}; +struct MOZ_NON_PARAM NonParam {}; +union MOZ_NON_PARAM NonParamUnion {}; +class MOZ_NON_PARAM NonParamClass {}; +enum MOZ_NON_PARAM NonParamEnum { X, Y, Z }; +enum class MOZ_NON_PARAM NonParamEnumClass { X, Y, Z }; + +struct HasNonParamStruct { NonParam x; int y; }; +union HasNonParamUnion { NonParam x; int y; }; +struct HasNonParamStructUnion { HasNonParamUnion z; }; + +#define MAYBE_STATIC +#include "NonParameterTestCases.h" +#undef MAYBE_STATIC + +// Do not check typedef and using. +typedef void (*funcTypeParam)(Param x); +typedef void (*funcTypeNonParam)(NonParam x); + +using usingFuncTypeParam = void (*)(Param x); +using usingFuncTypeNonParam = void (*)(NonParam x); + +class class_ +{ + explicit class_(Param x) {} + explicit class_(NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + explicit class_(HasNonParamStruct x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + explicit class_(HasNonParamUnion x) {} //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + explicit class_(HasNonParamStructUnion x) {} //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +#define MAYBE_STATIC +#include "NonParameterTestCases.h" +#undef MAYBE_STATIC +}; + +class classWithStatic +{ +#define MAYBE_STATIC static +#include "NonParameterTestCases.h" +#undef MAYBE_STATIC +}; + +template +class tmplClassForParam +{ +public: + void raw(T x) {} + void rawDefault(T x = T()) {} + void const_(const T x) {} + void ptr(T* x) {} + void ref(T& x) {} + void constRef(const T& x) {} + + void notCalled(T x) {} +}; + +template +class tmplClassForNonParam +{ +public: + void raw(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void rawDefault(T x = T()) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void const_(const T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void ptr(T* x) {} + void ref(T& x) {} + void constRef(const T& x) {} + + void notCalled(T x) {} +}; + +template +class tmplClassForHasNonParamStruct +{ +public: + void raw(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void rawDefault(T x = T()) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void const_(const T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void ptr(T* x) {} + void ref(T& x) {} + void constRef(const T& x) {} + + void notCalled(T x) {} +}; + +void testTemplateClass() +{ + tmplClassForParam paramClass; + Param param; + paramClass.raw(param); + paramClass.rawDefault(); + paramClass.const_(param); + paramClass.ptr(¶m); + paramClass.ref(param); + paramClass.constRef(param); + + tmplClassForNonParam nonParamClass; //expected-note 3 {{The bad argument was passed to 'tmplClassForNonParam' here}} + NonParam nonParam; + nonParamClass.raw(nonParam); + nonParamClass.rawDefault(); + nonParamClass.const_(nonParam); + nonParamClass.ptr(&nonParam); + nonParamClass.ref(nonParam); + nonParamClass.constRef(nonParam); + + tmplClassForHasNonParamStruct hasNonParamStructClass;//expected-note 3 {{The bad argument was passed to 'tmplClassForHasNonParamStruct' here}} + HasNonParamStruct hasNonParamStruct; + hasNonParamStructClass.raw(hasNonParamStruct); + hasNonParamStructClass.rawDefault(); + hasNonParamStructClass.const_(hasNonParamStruct); + hasNonParamStructClass.ptr(&hasNonParamStruct); + hasNonParamStructClass.ref(hasNonParamStruct); + hasNonParamStructClass.constRef(hasNonParamStruct); +} + +template +class NestedTemplateInner +{ +public: + void raw(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +}; + +template +class nestedTemplateOuter +{ +public: + void constRef(const T& x) { + NestedTemplateInner inner; //expected-note {{The bad argument was passed to 'NestedTemplateInner' here}} + inner.raw(x); + } +}; + +void testNestedTemplateClass() +{ + nestedTemplateOuter outer; + NonParam nonParam; + outer.constRef(nonParam); // FIXME: this line needs note "The bad argument was passed to 'constRef' here" +} + +template +void tmplFuncForParam(T x) {} +template +void tmplFuncForNonParam(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +template +void tmplFuncForNonParamImplicit(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +template +void tmplFuncForHasNonParamStruct(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +template +void tmplFuncForHasNonParamStructImplicit(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +void testTemplateFunc() +{ + Param param; + tmplFuncForParam(param); + + NonParam nonParam; + tmplFuncForNonParam(nonParam); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForNonParam' here" + tmplFuncForNonParamImplicit(nonParam); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForNonParamImplicit' here" + + HasNonParamStruct hasNonParamStruct; + tmplFuncForHasNonParamStruct(hasNonParamStruct); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForHasNonParamStruct' here" + tmplFuncForHasNonParamStructImplicit(hasNonParamStruct); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForHasNonParamStructImplicit' here" +} + +void testLambda() +{ + auto paramLambda = [](Param x) -> void {}; + auto nonParamLambda = [](NonParam x) -> void {}; //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + auto nonParamStructLambda = [](HasNonParamStruct x) -> void {}; //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + auto nonParamUnionLambda = [](HasNonParamUnion x) -> void {}; //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + auto nonParamStructUnionLambda = [](HasNonParamStructUnion x) -> void {}; //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + + (void)[](Param x) -> void {}; + (void)[](NonParam x) -> void {}; //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + (void)[](HasNonParamStruct x) -> void {}; //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + (void)[](HasNonParamUnion x) -> void {}; //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + (void)[](HasNonParamStructUnion x) -> void {}; //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +} diff --git a/build/clang-plugin/tests/TestNonTemporaryClass.cpp b/build/clang-plugin/tests/TestNonTemporaryClass.cpp new file mode 100644 index 000000000..682c8ad53 --- /dev/null +++ b/build/clang-plugin/tests/TestNonTemporaryClass.cpp @@ -0,0 +1,70 @@ +#define MOZ_NON_TEMPORARY_CLASS __attribute__((annotate("moz_non_temporary_class"))) +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +#include + +struct MOZ_NON_TEMPORARY_CLASS NonTemporary { + int i; + NonTemporary() {} + MOZ_IMPLICIT NonTemporary(int a) {} + NonTemporary(int a, int b) {} + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template +struct MOZ_NON_TEMPORARY_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void gobbleref(const NonTemporary&) { } + +template +void gobbleanyref(const T&) { } + +void misuseNonTemporaryClass(int len) { + NonTemporary invalid; + NonTemporary alsoInvalid[2]; + static NonTemporary invalidStatic; + static NonTemporary alsoInvalidStatic[2]; + + gobble(&invalid); + gobble(&invalidStatic); + gobble(&alsoInvalid[0]); + + gobbleref(NonTemporary()); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(NonTemporary(10, 20)); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(NonTemporary(10)); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(10); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleanyref(TemplateClass()); // expected-error {{variable of type 'TemplateClass' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + + gobble(new NonTemporary); + gobble(new NonTemporary[10]); + gobble(new TemplateClass); + gobble(len <= 5 ? &invalid : new NonTemporary); + + char buffer[sizeof(NonTemporary)]; + gobble(new (buffer) NonTemporary); +} + +void defaultArg(const NonTemporary& arg = NonTemporary()) { +} + +NonTemporary invalidStatic; +struct RandomClass { + NonTemporary nonstaticMember; // expected-note {{'RandomClass' is a non-temporary type because member 'nonstaticMember' is a non-temporary type 'NonTemporary'}} + static NonTemporary staticMember; +}; +struct MOZ_NON_TEMPORARY_CLASS RandomNonTemporaryClass { + NonTemporary nonstaticMember; + static NonTemporary staticMember; +}; + +struct BadInherit : NonTemporary {}; // expected-note {{'BadInherit' is a non-temporary type because it inherits from a non-temporary type 'NonTemporary'}} + +void useStuffWrongly() { + gobbleanyref(BadInherit()); // expected-error {{variable of type 'BadInherit' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleanyref(RandomClass()); // expected-error {{variable of type 'RandomClass' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} +} diff --git a/build/clang-plugin/tests/TestOverrideBaseCall.cpp b/build/clang-plugin/tests/TestOverrideBaseCall.cpp new file mode 100644 index 000000000..6fdaaad04 --- /dev/null +++ b/build/clang-plugin/tests/TestOverrideBaseCall.cpp @@ -0,0 +1,175 @@ +#define MOZ_REQUIRED_BASE_METHOD __attribute__((annotate("moz_required_base_method"))) + +class Base { +public: + virtual void fo() MOZ_REQUIRED_BASE_METHOD { + } + + virtual int foRet() MOZ_REQUIRED_BASE_METHOD { + return 0; + } +}; + +class BaseOne : public Base { +public: + virtual void fo() MOZ_REQUIRED_BASE_METHOD { + Base::fo(); + } +}; + +class BaseSecond : public Base { +public: + virtual void fo() MOZ_REQUIRED_BASE_METHOD { + Base::fo(); + } +}; + +class Deriv : public BaseOne, public BaseSecond { +public: + void func() { + } + + void fo() { + func(); + BaseSecond::fo(); + BaseOne::fo(); + } +}; + +class DerivSimple : public Base { +public: + void fo() { // expected-error {{Method Base::fo must be called in all overrides, but is not called in this override defined for class DerivSimple}} + } +}; + +class BaseVirtualOne : public virtual Base { +}; + +class BaseVirtualSecond: public virtual Base { +}; + +class DerivVirtual : public BaseVirtualOne, public BaseVirtualSecond { +public: + void fo() { + Base::fo(); + } +}; + +class DerivIf : public Base { +public: + void fo() { + if (true) { + Base::fo(); + } + } +}; + +class DerivIfElse : public Base { +public: + void fo() { + if (true) { + Base::fo(); + } else { + Base::fo(); + } + } +}; + +class DerivFor : public Base { +public: + void fo() { + for (int i = 0; i < 10; i++) { + Base::fo(); + } + } +}; + +class DerivDoWhile : public Base { +public: + void fo() { + do { + Base::fo(); + } while(false); + } +}; + +class DerivWhile : public Base { +public: + void fo() { + while (true) { + Base::fo(); + break; + } + } +}; + +class DerivAssignment : public Base { +public: + int foRet() { + return foRet(); + } +}; + +class BaseOperator { +private: + int value; +public: + BaseOperator() : value(0) { + } + virtual BaseOperator& operator++() MOZ_REQUIRED_BASE_METHOD { + value++; + return *this; + } +}; + +class DerivOperatorErr : public BaseOperator { +private: + int value; +public: + DerivOperatorErr() : value(0) { + } + DerivOperatorErr& operator++() { // expected-error {{Method BaseOperator::operator++ must be called in all overrides, but is not called in this override defined for class DerivOperatorErr}} + value++; + return *this; + } +}; + +class DerivOperator : public BaseOperator { +private: + int value; +public: + DerivOperator() : value(0) { + } + DerivOperator& operator++() { + BaseOperator::operator++(); + value++; + return *this; + } +}; + +class DerivPrime : public Base { +public: + void fo() { + Base::fo(); + } +}; + +class DerivSecondErr : public DerivPrime { +public: + void fo() { // expected-error {{Method Base::fo must be called in all overrides, but is not called in this override defined for class DerivSecondErr}} + } +}; + +class DerivSecond : public DerivPrime { +public: + void fo() { + Base::fo(); + } +}; + +class DerivSecondIndirect : public DerivPrime { +public: + void fo() { + DerivPrime::fo(); + } +}; diff --git a/build/clang-plugin/tests/TestOverrideBaseCallAnnotation.cpp b/build/clang-plugin/tests/TestOverrideBaseCallAnnotation.cpp new file mode 100644 index 000000000..e268122c6 --- /dev/null +++ b/build/clang-plugin/tests/TestOverrideBaseCallAnnotation.cpp @@ -0,0 +1,47 @@ +#define MOZ_REQUIRED_BASE_METHOD __attribute__((annotate("moz_required_base_method"))) + +class Base { +public: + virtual void fo() MOZ_REQUIRED_BASE_METHOD { + } +}; + +class BaseNonVirtual { +public: + void fo() MOZ_REQUIRED_BASE_METHOD { // expected-error {{MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods}} + } +}; + +class Deriv : public BaseNonVirtual { +public: + virtual void fo() MOZ_REQUIRED_BASE_METHOD { + } +}; + +class DerivVirtual : public Base { +public: + void fo() MOZ_REQUIRED_BASE_METHOD { + Base::fo(); + } +}; + +class BaseOperator { +public: + BaseOperator& operator++() MOZ_REQUIRED_BASE_METHOD { // expected-error {{MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods}} + return *this; + } +}; + +class DerivOperator : public BaseOperator { +public: + virtual DerivOperator& operator++() { + return *this; + } +}; + +class DerivPrimeOperator : public DerivOperator { +public: + DerivPrimeOperator& operator++() { + return *this; + } +}; diff --git a/build/clang-plugin/tests/TestRefCountedCopyConstructor.cpp b/build/clang-plugin/tests/TestRefCountedCopyConstructor.cpp new file mode 100644 index 000000000..d3bd73084 --- /dev/null +++ b/build/clang-plugin/tests/TestRefCountedCopyConstructor.cpp @@ -0,0 +1,25 @@ +// Implicit copy construct which is unused +class RC1 { + void AddRef(); + void Release(); + int mRefCnt; +}; + +// Explicit copy constructor which is used +class RC2 { +public: + RC2(); + RC2(const RC2&); +private: + void AddRef(); + void Release(); + int mRefCnt; +}; + +void f() { + RC1* r1 = new RC1(); + RC1* r1p = new RC1(*r1); // expected-error {{Invalid use of compiler-provided copy constructor on refcounted type}} expected-note {{The default copy constructor also copies the default mRefCnt property, leading to reference count imbalance issues. Please provide your own copy constructor which only copies the fields which need to be copied}} + + RC2* r2 = new RC2(); + RC2* r2p = new RC2(*r2); +} diff --git a/build/clang-plugin/tests/TestSprintfLiteral.cpp b/build/clang-plugin/tests/TestSprintfLiteral.cpp new file mode 100644 index 000000000..9b7675433 --- /dev/null +++ b/build/clang-plugin/tests/TestSprintfLiteral.cpp @@ -0,0 +1,47 @@ +#include + +void bad() { + char x[100]; + snprintf(x, sizeof(x), "bar"); // expected-error {{Use SprintfLiteral instead of snprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to snprintf accidentally.}} + snprintf(x, 100, "bar"); // expected-error {{Use SprintfLiteral instead of snprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to snprintf accidentally.}} + snprintf(x, 101, "bar"); // expected-error {{Use SprintfLiteral instead of snprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to snprintf accidentally.}} + const int hundred = 100; + snprintf(x, hundred, "bar"); // expected-error {{Use SprintfLiteral instead of snprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to snprintf accidentally.}} + const int hundredandone = 101; + snprintf(x, hundredandone, "bar"); // expected-error {{Use SprintfLiteral instead of snprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to snprintf accidentally.}} +} + +void ok() { + char x[100]; + int y; + snprintf(x, sizeof(y), "what"); + + snprintf(x, 50, "what"); + + int nothundred = 100; + nothundred = 99; + snprintf(x, nothundred, "what"); +} + +void vargs_bad(va_list args) { + char x[100]; + vsnprintf(x, sizeof(x), "bar", args); // expected-error {{Use VsprintfLiteral instead of vsnprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to vsnprintf accidentally.}} + vsnprintf(x, 100, "bar", args); // expected-error {{Use VsprintfLiteral instead of vsnprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to vsnprintf accidentally.}} + vsnprintf(x, 101, "bar", args); // expected-error {{Use VsprintfLiteral instead of vsnprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to vsnprintf accidentally.}} + const int hundred = 100; + vsnprintf(x, hundred, "bar", args); // expected-error {{Use VsprintfLiteral instead of vsnprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to vsnprintf accidentally.}} + const int hundredandone = 101; + vsnprintf(x, hundredandone, "bar", args); // expected-error {{Use VsprintfLiteral instead of vsnprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to vsnprintf accidentally.}} +} + +void vargs_good(va_list args) { + char x[100]; + int y; + vsnprintf(x, sizeof(y), "what", args); + + vsnprintf(x, 50, "what", args); + + int nothundred = 100; + nothundred = 99; + vsnprintf(x, nothundred, "what", args); +} diff --git a/build/clang-plugin/tests/TestStackClass.cpp b/build/clang-plugin/tests/TestStackClass.cpp new file mode 100644 index 000000000..41afa39e1 --- /dev/null +++ b/build/clang-plugin/tests/TestStackClass.cpp @@ -0,0 +1,50 @@ +#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) +#include + +struct MOZ_STACK_CLASS Stack { + int i; + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template +struct MOZ_STACK_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void misuseStackClass(int len) { + Stack valid; + Stack alsoValid[2]; + static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} + static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}} expected-note {{'Stack [2]' is a stack type because it is an array of stack type 'Stack'}} expected-note {{value incorrectly allocated in a global variable}} + + gobble(&valid); + gobble(¬Valid); + gobble(&alsoValid[0]); + + gobble(new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}} + gobble(new Stack[10]); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}} + gobble(new TemplateClass); // expected-error {{variable of type 'TemplateClass' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}} + gobble(len <= 5 ? &valid : new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}} + + char buffer[sizeof(Stack)]; + gobble(new (buffer) Stack); +} + +Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +struct RandomClass { + Stack nonstaticMember; // expected-note {{'RandomClass' is a stack type because member 'nonstaticMember' is a stack type 'Stack'}} + static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +}; +struct MOZ_STACK_CLASS RandomStackClass { + Stack nonstaticMember; + static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +}; + +struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack type because it inherits from a stack type 'Stack'}} +struct MOZ_STACK_CLASS GoodInherit : Stack {}; + +BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} diff --git a/build/clang-plugin/tests/TestTrivialCtorDtor.cpp b/build/clang-plugin/tests/TestTrivialCtorDtor.cpp new file mode 100644 index 000000000..cef06b0ac --- /dev/null +++ b/build/clang-plugin/tests/TestTrivialCtorDtor.cpp @@ -0,0 +1,83 @@ +#define MOZ_TRIVIAL_CTOR_DTOR __attribute__((annotate("moz_trivial_ctor_dtor"))) + +struct MOZ_TRIVIAL_CTOR_DTOR EmptyClass{}; + +template +struct MOZ_TRIVIAL_CTOR_DTOR TemplateEmptyClass{}; + +struct MOZ_TRIVIAL_CTOR_DTOR NonEmptyClass { + void *m; +}; + +template +struct MOZ_TRIVIAL_CTOR_DTOR TemplateNonEmptyClass { + T* m; +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadUserDefinedCtor { // expected-error {{class 'BadUserDefinedCtor' must have trivial constructors and destructors}} + BadUserDefinedCtor() {} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadUserDefinedDtor { // expected-error {{class 'BadUserDefinedDtor' must have trivial constructors and destructors}} + ~BadUserDefinedDtor() {} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadVirtualDtor { // expected-error {{class 'BadVirtualDtor' must have trivial constructors and destructors}} + virtual ~BadVirtualDtor() {} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR OkVirtualMember { + virtual void f(); +}; + +void foo(); +struct MOZ_TRIVIAL_CTOR_DTOR BadNonEmptyCtorDtor { // expected-error {{class 'BadNonEmptyCtorDtor' must have trivial constructors and destructors}} + BadNonEmptyCtorDtor() { foo(); } + ~BadNonEmptyCtorDtor() { foo(); } +}; + +struct NonTrivialCtor { + NonTrivialCtor() { foo(); } +}; + +struct NonTrivialDtor { + ~NonTrivialDtor() { foo(); } +}; + +struct VirtualMember { + virtual void f(); +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialCtorInBase : NonTrivialCtor { // expected-error {{class 'BadNonTrivialCtorInBase' must have trivial constructors and destructors}} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialDtorInBase : NonTrivialDtor { // expected-error {{class 'BadNonTrivialDtorInBase' must have trivial constructors and destructors}} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialCtorInMember { // expected-error {{class 'BadNonTrivialCtorInMember' must have trivial constructors and destructors}} + NonTrivialCtor m; +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialDtorInMember { // expected-error {{class 'BadNonTrivialDtorInMember' must have trivial constructors and destructors}} + NonTrivialDtor m; +}; + +struct MOZ_TRIVIAL_CTOR_DTOR OkVirtualMemberInMember { + VirtualMember m; +}; + +struct MOZ_TRIVIAL_CTOR_DTOR OkConstExprConstructor { + constexpr OkConstExprConstructor() {} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR OkConstExprConstructorInMember { + OkConstExprConstructor m; +}; + +// XXX: This error is unfortunate, but is unlikely to come up in real code. +// In this situation, it should be possible to define a constexpr constructor +// which explicitly initializes the members. +struct MOZ_TRIVIAL_CTOR_DTOR BadUnfortunateError { // expected-error {{class 'BadUnfortunateError' must have trivial constructors and destructors}} + OkConstExprConstructor m; + void *n; +}; diff --git a/build/clang-plugin/tests/moz.build b/build/clang-plugin/tests/moz.build new file mode 100644 index 000000000..3f7bdcba1 --- /dev/null +++ b/build/clang-plugin/tests/moz.build @@ -0,0 +1,45 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# dummy library name to avoid skipping building the sources here. +Library('clang-plugin-tests') + +SOURCES += [ + 'TestAssertWithAssignment.cpp', + 'TestBadImplicitConversionCtor.cpp', + 'TestCustomHeap.cpp', + 'TestExplicitOperatorBool.cpp', + 'TestGlobalClass.cpp', + 'TestHeapClass.cpp', + 'TestInheritTypeAnnotationsFromTemplateArgs.cpp', + 'TestKungFuDeathGrip.cpp', + 'TestMultipleAnnotations.cpp', + 'TestMustOverride.cpp', + 'TestMustUse.cpp', + 'TestNANTestingExpr.cpp', + 'TestNANTestingExprC.c', + 'TestNeedsNoVTableType.cpp', + 'TestNoAddRefReleaseOnReturn.cpp', + 'TestNoArithmeticExprInArgument.cpp', + 'TestNoAutoType.cpp', + 'TestNoDuplicateRefCntMember.cpp', + 'TestNoExplicitMoveConstructor.cpp', + 'TestNonHeapClass.cpp', + 'TestNonMemMovable.cpp', + 'TestNonMemMovableStd.cpp', + 'TestNonParameterChecker.cpp', + 'TestNonTemporaryClass.cpp', + 'TestNoRefcountedInsideLambdas.cpp', + 'TestOverrideBaseCall.cpp', + 'TestOverrideBaseCallAnnotation.cpp', + 'TestRefCountedCopyConstructor.cpp', + 'TestSprintfLiteral.cpp', + 'TestStackClass.cpp', + 'TestTrivialCtorDtor.cpp', +] + +DISABLE_STL_WRAPPING = True +NO_VISIBILITY_FLAGS = True -- cgit v1.2.3