diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-01-13 20:00:46 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-01-13 20:00:46 +0000 |
commit | 0414e226b73ef7952be3ef346c1c802e7f036f54 (patch) | |
tree | ff0114c0524108a01707e4101f3224db0d7fd01f | |
parent | 97b17066aaac3f1590a809d79abe98fde03821ec (diff) |
Vendor import of clang trunk r257626:vendor/clang/clang-trunk-r257626
Notes
Notes:
svn path=/vendor/clang/dist/; revision=293840
svn path=/vendor/clang/clang-trunk-r257626/; revision=293841; tag=vendor/clang/clang-trunk-r257626
277 files changed, 9481 insertions, 1329 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index b66009e909a1..abf92948bb42 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -131,6 +131,7 @@ class ASTContext : public RefCountedBase<ASTContext> { mutable llvm::FoldingSet<AutoType> AutoTypes; mutable llvm::FoldingSet<AtomicType> AtomicTypes; llvm::FoldingSet<AttributedType> AttributedTypes; + mutable llvm::FoldingSet<PipeType> PipeTypes; mutable llvm::FoldingSet<QualifiedTemplateName> QualifiedTemplateNames; mutable llvm::FoldingSet<DependentTemplateName> DependentTemplateNames; @@ -1079,6 +1080,9 @@ public: /// blocks. QualType getBlockDescriptorType() const; + /// \brief Return pipe type for the specified type. + QualType getPipeType(QualType T) const; + /// Gets the struct used to keep track of the extended descriptor for /// pointer to blocks. QualType getBlockDescriptorExtendedType() const; @@ -2279,9 +2283,13 @@ public: /// \brief Make an APSInt of the appropriate width and signedness for the /// given \p Value and integer \p Type. llvm::APSInt MakeIntValue(uint64_t Value, QualType Type) const { - llvm::APSInt Res(getIntWidth(Type), - !Type->isSignedIntegerOrEnumerationType()); + // If Type is a signed integer type larger than 64 bits, we need to be sure + // to sign extend Res appropriately. + llvm::APSInt Res(64, !Type->isSignedIntegerOrEnumerationType()); Res = Value; + unsigned Width = getIntWidth(Type); + if (Width != Res.getBitWidth()) + return Res.extOrTrunc(Width); return Res; } diff --git a/include/clang/AST/ASTMutationListener.h b/include/clang/AST/ASTMutationListener.h index 3ff392de11a7..cf3b55d7b2c7 100644 --- a/include/clang/AST/ASTMutationListener.h +++ b/include/clang/AST/ASTMutationListener.h @@ -29,6 +29,7 @@ namespace clang { class ObjCContainerDecl; class ObjCInterfaceDecl; class ObjCPropertyDecl; + class ParmVarDecl; class QualType; class RecordDecl; class TagDecl; @@ -88,6 +89,9 @@ public: /// \brief A function template's definition was instantiated. virtual void FunctionDefinitionInstantiated(const FunctionDecl *D) {} + /// \brief A default argument was instantiated. + virtual void DefaultArgumentInstantiated(const ParmVarDecl *D) {} + /// \brief A new objc category class was added for an interface. virtual void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, const ObjCInterfaceDecl *IFD) {} diff --git a/include/clang/AST/BuiltinTypes.def b/include/clang/AST/BuiltinTypes.def index 85e237a2bf04..a08a6839024b 100644 --- a/include/clang/AST/BuiltinTypes.def +++ b/include/clang/AST/BuiltinTypes.def @@ -1,4 +1,4 @@ -//===-- BuiltinTypeNodes.def - Metadata about BuiltinTypes ------*- C++ -*-===// +//===-- BuiltinTypes.def - Metadata about BuiltinTypes ----------*- C++ -*-===// // // The LLVM Compiler Infrastructure // diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 046ce70a343b..029c1182f26e 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -2440,10 +2440,9 @@ class IndirectFieldDecl : public ValueDecl, NamedDecl **Chaining; unsigned ChainingSize; - IndirectFieldDecl(DeclContext *DC, SourceLocation L, + IndirectFieldDecl(ASTContext &C, DeclContext *DC, SourceLocation L, DeclarationName N, QualType T, - NamedDecl **CH, unsigned CHS) - : ValueDecl(IndirectField, DC, L, N, T), Chaining(CH), ChainingSize(CHS) {} + NamedDecl **CH, unsigned CHS); public: static IndirectFieldDecl *Create(ASTContext &C, DeclContext *DC, diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index 05b2a1280fab..2d6e84a68aa5 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -113,6 +113,9 @@ public: /// Tags, declared with 'struct foo;' and referenced with /// 'struct foo'. All tags are also types. This is what /// elaborated-type-specifiers look for in C. + /// This also contains names that conflict with tags in the + /// same scope but that are otherwise ordinary names (non-type + /// template parameters and indirect field declarations). IDNS_Tag = 0x0002, /// Types, declared with 'struct foo', typedefs, etc. @@ -131,7 +134,7 @@ public: IDNS_Namespace = 0x0010, /// Ordinary names. In C, everything that's not a label, tag, - /// or member ends up here. + /// member, or function-local extern ends up here. IDNS_Ordinary = 0x0020, /// Objective C \@protocol. @@ -160,7 +163,9 @@ public: /// This declaration is a function-local extern declaration of a /// variable or function. This may also be IDNS_Ordinary if it - /// has been declared outside any function. + /// has been declared outside any function. These act mostly like + /// invisible friend declarations, but are also visible to unqualified + /// lookup within the scope of the declaring function. IDNS_LocalExtern = 0x0800 }; diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 095dd6a1ef34..38733eee82c3 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -1292,6 +1292,7 @@ public: enum CharacterKind { Ascii, Wide, + UTF8, UTF16, UTF32 }; diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 0608abac1fe9..682127498647 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -951,15 +951,9 @@ public: /// This wraps up a function call argument that was created from the /// corresponding parameter's default argument, when the call did not /// explicitly supply arguments for all of the parameters. -class CXXDefaultArgExpr final - : public Expr, - private llvm::TrailingObjects<CXXDefaultArgExpr, Expr *> { +class CXXDefaultArgExpr final : public Expr { /// \brief The parameter whose default is being used. - /// - /// When the bit is set, the subexpression is stored after the - /// CXXDefaultArgExpr itself. When the bit is clear, the parameter's - /// actual default expression is the subexpression. - llvm::PointerIntPair<ParmVarDecl *, 1, bool> Param; + ParmVarDecl *Param; /// \brief The location where the default argument expression was used. SourceLocation Loc; @@ -971,16 +965,7 @@ class CXXDefaultArgExpr final : param->getDefaultArg()->getType(), param->getDefaultArg()->getValueKind(), param->getDefaultArg()->getObjectKind(), false, false, false, false), - Param(param, false), Loc(Loc) { } - - CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *param, - Expr *SubExpr) - : Expr(SC, SubExpr->getType(), - SubExpr->getValueKind(), SubExpr->getObjectKind(), - false, false, false, false), - Param(param, true), Loc(Loc) { - *getTrailingObjects<Expr *>() = SubExpr; - } + Param(param), Loc(Loc) { } public: CXXDefaultArgExpr(EmptyShell Empty) : Expr(CXXDefaultArgExprClass, Empty) {} @@ -992,24 +977,15 @@ public: return new (C) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param); } - // \p Param is the parameter whose default argument is used by this - // expression, and \p SubExpr is the expression that will actually be used. - static CXXDefaultArgExpr *Create(const ASTContext &C, SourceLocation Loc, - ParmVarDecl *Param, Expr *SubExpr); - // Retrieve the parameter that the argument was created from. - const ParmVarDecl *getParam() const { return Param.getPointer(); } - ParmVarDecl *getParam() { return Param.getPointer(); } + const ParmVarDecl *getParam() const { return Param; } + ParmVarDecl *getParam() { return Param; } // Retrieve the actual argument to the function call. const Expr *getExpr() const { - if (Param.getInt()) - return *getTrailingObjects<Expr *>(); return getParam()->getDefaultArg(); } Expr *getExpr() { - if (Param.getInt()) - return *getTrailingObjects<Expr *>(); return getParam()->getDefaultArg(); } @@ -1033,7 +1009,6 @@ public: return child_range(child_iterator(), child_iterator()); } - friend TrailingObjects; friend class ASTStmtReader; friend class ASTStmtWriter; }; diff --git a/include/clang/AST/OperationKinds.h b/include/clang/AST/OperationKinds.h index 2235c1012fb7..102bbc21edb3 100644 --- a/include/clang/AST/OperationKinds.h +++ b/include/clang/AST/OperationKinds.h @@ -185,7 +185,11 @@ enum CastKind { /// CK_FloatingToBoolean - Floating point to boolean. /// (bool) f CK_FloatingToBoolean, - + + // CK_BooleanToSignedIntegral - Convert a boolean to -1 or 0 for true and + // false, respectively. + CK_BooleanToSignedIntegral, + /// CK_FloatingCast - Casting between floating types of different size. /// (double) f /// (float) ld diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index e6f758364a85..0c25a45c1cec 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -978,6 +978,8 @@ DEF_TRAVERSE_TYPE(ObjCObjectPointerType, DEF_TRAVERSE_TYPE(AtomicType, { TRY_TO(TraverseType(T->getValueType())); }) +DEF_TRAVERSE_TYPE(PipeType, { TRY_TO(TraverseType(T->getElementType())); }) + #undef DEF_TRAVERSE_TYPE // ----------------- TypeLoc traversal ----------------- @@ -1206,6 +1208,8 @@ DEF_TRAVERSE_TYPELOC(ObjCObjectPointerType, DEF_TRAVERSE_TYPELOC(AtomicType, { TRY_TO(TraverseTypeLoc(TL.getValueLoc())); }) +DEF_TRAVERSE_TYPELOC(PipeType, { TRY_TO(TraverseTypeLoc(TL.getValueLoc())); }) + #undef DEF_TRAVERSE_TYPELOC // ----------------- Decl traversal ----------------- diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index e48b7dcc28f5..d3950e92cf0d 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -130,7 +130,7 @@ protected: friend class CharacterLiteral; unsigned : NumExprBits; - unsigned Kind : 2; + unsigned Kind : 3; }; enum APFloatSemantics { diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 0c08130da621..d63b2c43d553 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -1721,6 +1721,7 @@ public: bool isNDRangeT() const; // OpenCL ndrange_t bool isReserveIDT() const; // OpenCL reserve_id_t + bool isPipeType() const; // OpenCL pipe type bool isOpenCLSpecificType() const; // Any OpenCL specific type /// Determines if this type, which must satisfy @@ -5015,6 +5016,41 @@ class AtomicType : public Type, public llvm::FoldingSetNode { } }; +/// PipeType - OpenCL20. +class PipeType : public Type, public llvm::FoldingSetNode { + QualType ElementType; + + PipeType(QualType elemType, QualType CanonicalPtr) : + Type(Pipe, CanonicalPtr, elemType->isDependentType(), + elemType->isInstantiationDependentType(), + elemType->isVariablyModifiedType(), + elemType->containsUnexpandedParameterPack()), + ElementType(elemType) {} + friend class ASTContext; // ASTContext creates these. + +public: + + QualType getElementType() const { return ElementType; } + + bool isSugared() const { return false; } + + QualType desugar() const { return QualType(this, 0); } + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getElementType()); + } + + static void Profile(llvm::FoldingSetNodeID &ID, QualType T) { + ID.AddPointer(T.getAsOpaquePtr()); + } + + + static bool classof(const Type *T) { + return T->getTypeClass() == Pipe; + } + +}; + /// A qualifier set is used to build a set of qualifiers. class QualifierCollector : public Qualifiers { public: @@ -5461,9 +5497,13 @@ inline bool Type::isImageType() const { isImage1dBufferT(); } +inline bool Type::isPipeType() const { + return isa<PipeType>(CanonicalType); +} + inline bool Type::isOpenCLSpecificType() const { return isSamplerT() || isEventT() || isImageType() || isClkEventT() || - isQueueT() || isNDRangeT() || isReserveIDT(); + isQueueT() || isNDRangeT() || isReserveIDT() || isPipeType(); } inline bool Type::isTemplateTypeParmType() const { diff --git a/include/clang/AST/TypeLoc.h b/include/clang/AST/TypeLoc.h index 26feda5d7668..29035a41776e 100644 --- a/include/clang/AST/TypeLoc.h +++ b/include/clang/AST/TypeLoc.h @@ -2033,7 +2033,26 @@ public: } }; +struct PipeTypeLocInfo { + SourceLocation KWLoc; +}; + +class PipeTypeLoc : public ConcreteTypeLoc<UnqualTypeLoc, PipeTypeLoc, PipeType, + PipeTypeLocInfo> { +public: + TypeLoc getValueLoc() const { return this->getInnerTypeLoc(); } + + SourceRange getLocalSourceRange() const { return SourceRange(getKWLoc()); } + + SourceLocation getKWLoc() const { return this->getLocalData()->KWLoc; } + void setKWLoc(SourceLocation Loc) { this->getLocalData()->KWLoc = Loc; } + void initializeLocal(ASTContext &Context, SourceLocation Loc) { + setKWLoc(Loc); + } + + QualType getInnerType() const { return this->getTypePtr()->getElementType(); } +}; } #endif diff --git a/include/clang/AST/TypeNodes.def b/include/clang/AST/TypeNodes.def index 2549f0bf50c5..8caf1024142d 100644 --- a/include/clang/AST/TypeNodes.def +++ b/include/clang/AST/TypeNodes.def @@ -104,6 +104,7 @@ NON_CANONICAL_UNLESS_DEPENDENT_TYPE(PackExpansion, Type) TYPE(ObjCObject, Type) TYPE(ObjCInterface, ObjCObjectType) TYPE(ObjCObjectPointer, Type) +TYPE(Pipe, Type) TYPE(Atomic, Type) #ifdef LAST_TYPE diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h index d49909183fd4..1d1d7952c166 100644 --- a/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -560,10 +560,10 @@ bool matchesFirstInPointerRange(const MatcherT &Matcher, IteratorT Start, // Metafunction to determine if type T has a member called // getDecl. -#if defined(_MSC_VER) && (_MSC_VER < 1900) && !defined(__clang__) -// For old versions of MSVC, we use a weird nonstandard __if_exists -// statement, since before MSVC2015, it was not standards-conformant -// enough to compile the usual code below. +#if defined(_MSC_VER) && !defined(__clang__) +// For MSVC, we use a weird nonstandard __if_exists statement, as it +// is not standards-conformant enough to properly compile the standard +// code below. (At least up through MSVC 2015 require this workaround) template <typename T> struct has_getDecl { __if_exists(T::getDecl) { enum { value = 1 }; diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td index ce270bfffc69..b04498f3188c 100644 --- a/include/clang/Basic/DiagnosticDriverKinds.td +++ b/include/clang/Basic/DiagnosticDriverKinds.td @@ -22,6 +22,7 @@ def err_drv_unknown_stdin_type_clang_cl : Error< def err_drv_unknown_language : Error<"language not recognized: '%0'">; def err_drv_invalid_arch_name : Error< "invalid arch name '%0'">; +def err_drv_cuda_bad_gpu_arch : Error<"Unsupported CUDA gpu architecture: %0">; def err_drv_invalid_thread_model_for_target : Error< "invalid thread model '%0' in '%1' for this target">; def err_drv_invalid_linker_name : Error< diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 8e5f57d6d890..2e4e57b63b8e 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -616,6 +616,7 @@ def Most : DiagGroup<"most", [ CharSubscript, Comment, DeleteNonVirtualDtor, + ForLoopAnalysis, Format, Implicit, InfiniteRecursion, diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h index a675dfabe46d..312b71f4064d 100644 --- a/include/clang/Basic/DiagnosticIDs.h +++ b/include/clang/Basic/DiagnosticIDs.h @@ -29,7 +29,7 @@ namespace clang { enum { DIAG_START_COMMON = 0, DIAG_START_DRIVER = DIAG_START_COMMON + 300, - DIAG_START_FRONTEND = DIAG_START_DRIVER + 100, + DIAG_START_FRONTEND = DIAG_START_DRIVER + 200, DIAG_START_SERIALIZATION = DIAG_START_FRONTEND + 100, DIAG_START_LEX = DIAG_START_SERIALIZATION + 120, DIAG_START_PARSE = DIAG_START_LEX + 300, diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index ed6ff20f5c42..2fc9664f49e5 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -490,6 +490,8 @@ def warn_pragma_diagnostic_unknown_warning : // - #pragma __debug def warn_pragma_debug_unexpected_command : Warning< "unexpected debug command '%0'">, InGroup<IgnoredPragmas>; +def warn_pragma_debug_missing_argument : Warning< + "missing argument to debug command '%0'">, InGroup<IgnoredPragmas>; def err_defined_macro_name : Error<"'defined' cannot be used as a macro name">; def err_paste_at_start : Error< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 59f5095d6467..6ba482c78e4a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1,4 +1,3 @@ - //==--- DiagnosticSemaKinds.td - libsema diagnostics ----------------------===// // // The LLVM Compiler Infrastructure @@ -7643,6 +7642,10 @@ def err_wrong_sampler_addressspace: Error< "sampler type cannot be used with the __local and __global address space qualifiers">; def err_opencl_global_invalid_addr_space : Error< "program scope variable must reside in %0 address space">; +def err_missing_actual_pipe_type : Error< + "missing actual type specifier for pipe">; +def err_reference_pipe_type : Error < + "pipes packet types cannot be of reference type">; def err_opencl_no_main : Error<"%select{function|kernel}0 cannot be called 'main'">; def err_opencl_kernel_attr : Error<"attribute %0 can only be applied to a kernel function">; diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index 1d59d64d6bc7..e284171f7773 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -36,6 +36,11 @@ namespace clang { TSS_unsigned }; + enum TypeSpecifiersPipe { + TSP_unspecified, + TSP_pipe + }; + /// \brief Specifies the kind of type. enum TypeSpecifierType { TST_unspecified, diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index 9252d9947a94..026945141d24 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -519,6 +519,8 @@ KEYWORD(vec_step , KEYOPENCL|KEYALTIVEC|KEYZVECTOR) // OpenMP Type Traits KEYWORD(__builtin_omp_required_simd_align, KEYALL) +KEYWORD(pipe , KEYOPENCL) + // Borland Extensions. KEYWORD(__pascal , KEYALL) @@ -697,6 +699,11 @@ ANNOTATION(pragma_parser_crash) // handles them. ANNOTATION(pragma_captured) +// Annotation for #pragma clang __debug dump... +// The lexer produces these so that the parser and semantic analysis can +// look up and dump the operand. +ANNOTATION(pragma_dump) + // Annotation for #pragma ms_struct... // The lexer produces these so that they only take effect when the parser // handles them. diff --git a/include/clang/Basic/VirtualFileSystem.h b/include/clang/Basic/VirtualFileSystem.h index 1df4947dd7e8..bab88c90b0a8 100644 --- a/include/clang/Basic/VirtualFileSystem.h +++ b/include/clang/Basic/VirtualFileSystem.h @@ -299,10 +299,7 @@ public: llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override { return WorkingDirectory; } - std::error_code setCurrentWorkingDirectory(const Twine &Path) override { - WorkingDirectory = Path.str(); - return std::error_code(); - } + std::error_code setCurrentWorkingDirectory(const Twine &Path) override; }; /// \brief Get a globally unique ID for a virtual file or directory. diff --git a/include/clang/CodeGen/BackendUtil.h b/include/clang/CodeGen/BackendUtil.h index ba5dc3939ced..d375a7881313 100644 --- a/include/clang/CodeGen/BackendUtil.h +++ b/include/clang/CodeGen/BackendUtil.h @@ -33,12 +33,10 @@ namespace clang { Backend_EmitObj ///< Emit native object files }; - void - EmitBackendOutput(DiagnosticsEngine &Diags, const CodeGenOptions &CGOpts, - const TargetOptions &TOpts, const LangOptions &LOpts, - StringRef TDesc, llvm::Module *M, BackendAction Action, - raw_pwrite_stream *OS, - std::unique_ptr<llvm::FunctionInfoIndex> Index = nullptr); + void EmitBackendOutput(DiagnosticsEngine &Diags, const CodeGenOptions &CGOpts, + const TargetOptions &TOpts, const LangOptions &LOpts, + StringRef TDesc, llvm::Module *M, BackendAction Action, + raw_pwrite_stream *OS); } #endif diff --git a/include/clang/Driver/Action.h b/include/clang/Driver/Action.h index fc31d4b0dec2..c5b0f4755092 100644 --- a/include/clang/Driver/Action.h +++ b/include/clang/Driver/Action.h @@ -15,6 +15,9 @@ #include "llvm/ADT/SmallVector.h" namespace llvm { + +class StringRef; + namespace opt { class Arg; } @@ -32,6 +35,9 @@ namespace driver { /// single primary output, at least in terms of controlling the /// compilation. Actions can produce auxiliary files, but can only /// produce a single output to feed into subsequent actions. +/// +/// Actions are usually owned by a Compilation, which creates new +/// actions via MakeAction(). class Action { public: typedef ActionList::size_type size_type; @@ -70,27 +76,20 @@ private: ActionList Inputs; - unsigned OwnsInputs : 1; - protected: - Action(ActionClass Kind, types::ID Type) - : Kind(Kind), Type(Type), OwnsInputs(true) {} - Action(ActionClass Kind, std::unique_ptr<Action> Input, types::ID Type) - : Kind(Kind), Type(Type), Inputs(1, Input.release()), OwnsInputs(true) { - } - Action(ActionClass Kind, std::unique_ptr<Action> Input) - : Kind(Kind), Type(Input->getType()), Inputs(1, Input.release()), - OwnsInputs(true) {} + Action(ActionClass Kind, types::ID Type) : Action(Kind, ActionList(), Type) {} + Action(ActionClass Kind, Action *Input, types::ID Type) + : Action(Kind, ActionList({Input}), Type) {} + Action(ActionClass Kind, Action *Input) + : Action(Kind, ActionList({Input}), Input->getType()) {} Action(ActionClass Kind, const ActionList &Inputs, types::ID Type) - : Kind(Kind), Type(Type), Inputs(Inputs), OwnsInputs(true) {} + : Kind(Kind), Type(Type), Inputs(Inputs) {} + public: virtual ~Action(); const char *getClassName() const { return Action::getClassName(getKind()); } - bool getOwnsInputs() { return OwnsInputs; } - void setOwnsInputs(bool Value) { OwnsInputs = Value; } - ActionClass getKind() const { return Kind; } types::ID getType() const { return Type; } @@ -126,7 +125,7 @@ class BindArchAction : public Action { const char *ArchName; public: - BindArchAction(std::unique_ptr<Action> Input, const char *ArchName); + BindArchAction(Action *Input, const char *ArchName); const char *getArchName() const { return ArchName; } @@ -137,19 +136,24 @@ public: class CudaDeviceAction : public Action { virtual void anchor(); - /// GPU architecture to bind -- e.g 'sm_35'. + /// GPU architecture to bind. Always of the form /sm_\d+/. const char *GpuArchName; /// True when action results are not consumed by the host action (e.g when /// -fsyntax-only or --cuda-device-only options are used). bool AtTopLevel; public: - CudaDeviceAction(std::unique_ptr<Action> Input, const char *ArchName, - bool AtTopLevel); + CudaDeviceAction(Action *Input, const char *ArchName, bool AtTopLevel); const char *getGpuArchName() const { return GpuArchName; } + + /// Gets the compute_XX that corresponds to getGpuArchName(). + const char *getComputeArchName() const; + bool isAtTopLevel() const { return AtTopLevel; } + static bool IsValidGpuArchName(llvm::StringRef ArchName); + static bool classof(const Action *A) { return A->getKind() == CudaDeviceClass; } @@ -160,9 +164,7 @@ class CudaHostAction : public Action { ActionList DeviceActions; public: - CudaHostAction(std::unique_ptr<Action> Input, - const ActionList &DeviceActions); - ~CudaHostAction() override; + CudaHostAction(Action *Input, const ActionList &DeviceActions); const ActionList &getDeviceActions() const { return DeviceActions; } @@ -172,7 +174,7 @@ public: class JobAction : public Action { virtual void anchor(); protected: - JobAction(ActionClass Kind, std::unique_ptr<Action> Input, types::ID Type); + JobAction(ActionClass Kind, Action *Input, types::ID Type); JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type); public: @@ -185,7 +187,7 @@ public: class PreprocessJobAction : public JobAction { void anchor() override; public: - PreprocessJobAction(std::unique_ptr<Action> Input, types::ID OutputType); + PreprocessJobAction(Action *Input, types::ID OutputType); static bool classof(const Action *A) { return A->getKind() == PreprocessJobClass; @@ -195,7 +197,7 @@ public: class PrecompileJobAction : public JobAction { void anchor() override; public: - PrecompileJobAction(std::unique_ptr<Action> Input, types::ID OutputType); + PrecompileJobAction(Action *Input, types::ID OutputType); static bool classof(const Action *A) { return A->getKind() == PrecompileJobClass; @@ -205,7 +207,7 @@ public: class AnalyzeJobAction : public JobAction { void anchor() override; public: - AnalyzeJobAction(std::unique_ptr<Action> Input, types::ID OutputType); + AnalyzeJobAction(Action *Input, types::ID OutputType); static bool classof(const Action *A) { return A->getKind() == AnalyzeJobClass; @@ -215,7 +217,7 @@ public: class MigrateJobAction : public JobAction { void anchor() override; public: - MigrateJobAction(std::unique_ptr<Action> Input, types::ID OutputType); + MigrateJobAction(Action *Input, types::ID OutputType); static bool classof(const Action *A) { return A->getKind() == MigrateJobClass; @@ -225,7 +227,7 @@ public: class CompileJobAction : public JobAction { void anchor() override; public: - CompileJobAction(std::unique_ptr<Action> Input, types::ID OutputType); + CompileJobAction(Action *Input, types::ID OutputType); static bool classof(const Action *A) { return A->getKind() == CompileJobClass; @@ -235,7 +237,7 @@ public: class BackendJobAction : public JobAction { void anchor() override; public: - BackendJobAction(std::unique_ptr<Action> Input, types::ID OutputType); + BackendJobAction(Action *Input, types::ID OutputType); static bool classof(const Action *A) { return A->getKind() == BackendJobClass; @@ -245,7 +247,7 @@ public: class AssembleJobAction : public JobAction { void anchor() override; public: - AssembleJobAction(std::unique_ptr<Action> Input, types::ID OutputType); + AssembleJobAction(Action *Input, types::ID OutputType); static bool classof(const Action *A) { return A->getKind() == AssembleJobClass; @@ -285,8 +287,7 @@ public: class VerifyJobAction : public JobAction { void anchor() override; public: - VerifyJobAction(ActionClass Kind, std::unique_ptr<Action> Input, - types::ID Type); + VerifyJobAction(ActionClass Kind, Action *Input, types::ID Type); static bool classof(const Action *A) { return A->getKind() == VerifyDebugInfoJobClass || A->getKind() == VerifyPCHJobClass; @@ -296,7 +297,7 @@ public: class VerifyDebugInfoJobAction : public VerifyJobAction { void anchor() override; public: - VerifyDebugInfoJobAction(std::unique_ptr<Action> Input, types::ID Type); + VerifyDebugInfoJobAction(Action *Input, types::ID Type); static bool classof(const Action *A) { return A->getKind() == VerifyDebugInfoJobClass; } @@ -305,7 +306,7 @@ public: class VerifyPCHJobAction : public VerifyJobAction { void anchor() override; public: - VerifyPCHJobAction(std::unique_ptr<Action> Input, types::ID Type); + VerifyPCHJobAction(Action *Input, types::ID Type); static bool classof(const Action *A) { return A->getKind() == VerifyPCHJobClass; } diff --git a/include/clang/Driver/Compilation.h b/include/clang/Driver/Compilation.h index 12ff068d133c..3ed19135043a 100644 --- a/include/clang/Driver/Compilation.h +++ b/include/clang/Driver/Compilation.h @@ -48,7 +48,12 @@ class Compilation { /// own argument translation. llvm::opt::DerivedArgList *TranslatedArgs; - /// The list of actions. + /// The list of actions we've created via MakeAction. This is not accessible + /// to consumers; it's here just to manage ownership. + std::vector<std::unique_ptr<Action>> AllActions; + + /// The list of actions. This is maintained and modified by consumers, via + /// getActions(). ActionList Actions; /// The root list of jobs. @@ -105,6 +110,15 @@ public: ActionList &getActions() { return Actions; } const ActionList &getActions() const { return Actions; } + /// Creates a new Action owned by this Compilation. + /// + /// The new Action is *not* added to the list returned by getActions(). + template <typename T, typename... Args> T *MakeAction(Args &&... Arg) { + T *RawPtr = new T(std::forward<Args>(Arg)...); + AllActions.push_back(std::unique_ptr<Action>(RawPtr)); + return RawPtr; + } + JobList &getJobs() { return Jobs; } const JobList &getJobs() const { return Jobs; } diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h index c9940ba5501f..a229779e1aa1 100644 --- a/include/clang/Driver/Driver.h +++ b/include/clang/Driver/Driver.h @@ -18,10 +18,10 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" -#include "llvm/Support/Path.h" // FIXME: Kill when CompilationInfo -#include <memory> - // lands. +#include "llvm/Support/Path.h" // FIXME: Kill when CompilationInfo lands. + #include <list> +#include <memory> #include <set> #include <string> @@ -375,20 +375,16 @@ public: /// ConstructAction - Construct the appropriate action to do for /// \p Phase on the \p Input, taking in to account arguments /// like -fsyntax-only or --analyze. - std::unique_ptr<Action> - ConstructPhaseAction(const ToolChain &TC, const llvm::opt::ArgList &Args, - phases::ID Phase, std::unique_ptr<Action> Input) const; + Action *ConstructPhaseAction(Compilation &C, const ToolChain &TC, + const llvm::opt::ArgList &Args, phases::ID Phase, + Action *Input) const; /// BuildJobsForAction - Construct the jobs to perform for the - /// action \p A. - void BuildJobsForAction(Compilation &C, - const Action *A, - const ToolChain *TC, - const char *BoundArch, - bool AtTopLevel, - bool MultipleArchs, - const char *LinkingOutput, - InputInfo &Result) const; + /// action \p A and return an InputInfo for the result of running \p A. + InputInfo BuildJobsForAction(Compilation &C, const Action *A, + const ToolChain *TC, const char *BoundArch, + bool AtTopLevel, bool MultipleArchs, + const char *LinkingOutput) const; /// Returns the default name for linked images (e.g., "a.out"). const char *getDefaultImageName() const; diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index e219a9b76b79..e4279e80d847 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -1906,13 +1906,13 @@ def _ : Joined<["--"], "">, Flags<[Unsupported]>; def mieee_rnd_near : Flag<["-"], "mieee-rnd-near">, Group<m_hexagon_Features_Group>; def mv4 : Flag<["-"], "mv4">, Group<m_hexagon_Features_Group>, - Alias<mcpu_EQ>, AliasArgs<["v4"]>; + Alias<mcpu_EQ>, AliasArgs<["hexagonv4"]>; def mv5 : Flag<["-"], "mv5">, Group<m_hexagon_Features_Group>, Alias<mcpu_EQ>, - AliasArgs<["v5"]>; + AliasArgs<["hexagonv5"]>; def mv55 : Flag<["-"], "mv55">, Group<m_hexagon_Features_Group>, - Alias<mcpu_EQ>, AliasArgs<["v55"]>; + Alias<mcpu_EQ>, AliasArgs<["hexagonv55"]>; def mv60 : Flag<["-"], "mv60">, Group<m_hexagon_Features_Group>, - Alias<mcpu_EQ>, AliasArgs<["v60"]>; + Alias<mcpu_EQ>, AliasArgs<["hexagonv60"]>; def mhexagon_hvx : Flag<["-"], "mhvx">, Group<m_hexagon_Features_Group>, Flags<[CC1Option]>, HelpText<"Enable Hexagon Vector eXtensions">; def mno_hexagon_hvx : Flag<["-"], "mno-hvx">, Group<m_hexagon_Features_Group>, diff --git a/include/clang/Driver/ToolChain.h b/include/clang/Driver/ToolChain.h index ed73107940ce..7e68d0a59a09 100644 --- a/include/clang/Driver/ToolChain.h +++ b/include/clang/Driver/ToolChain.h @@ -134,7 +134,7 @@ public: StringRef getOS() const { return Triple.getOSName(); } /// \brief Provide the default architecture name (as expected by -arch) for - /// this toolchain. Note t + /// this toolchain. StringRef getDefaultUniversalArchName() const; std::string getTripleString() const { diff --git a/include/clang/Lex/LiteralSupport.h b/include/clang/Lex/LiteralSupport.h index 5210e3f2e1c5..d568614e2ae4 100644 --- a/include/clang/Lex/LiteralSupport.h +++ b/include/clang/Lex/LiteralSupport.h @@ -166,6 +166,7 @@ public: bool hadError() const { return HadError; } bool isAscii() const { return Kind == tok::char_constant; } bool isWide() const { return Kind == tok::wide_char_constant; } + bool isUTF8() const { return Kind == tok::utf8_char_constant; } bool isUTF16() const { return Kind == tok::utf16_char_constant; } bool isUTF32() const { return Kind == tok::utf32_char_constant; } bool isMultiChar() const { return IsMultiChar; } diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 82b779879c20..00885a5c7103 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -502,6 +502,10 @@ private: void HandlePragmaAlign(); /// \brief Handle the annotation token produced for + /// #pragma clang __debug dump... + void HandlePragmaDump(); + + /// \brief Handle the annotation token produced for /// #pragma weak id... void HandlePragmaWeak(); @@ -1640,13 +1644,22 @@ private: /// A SmallVector of types. typedef SmallVector<ParsedType, 12> TypeVector; - StmtResult ParseStatement(SourceLocation *TrailingElseLoc = nullptr); + StmtResult ParseStatement(SourceLocation *TrailingElseLoc = nullptr, + bool AllowOpenMPStandalone = false); + enum AllowedContsructsKind { + /// \brief Allow any declarations, statements, OpenMP directives. + ACK_Any, + /// \brief Allow only statements and non-standalone OpenMP directives. + ACK_StatementsOpenMPNonStandalone, + /// \brief Allow statements and all executable OpenMP directives + ACK_StatementsOpenMPAnyExecutable + }; StmtResult - ParseStatementOrDeclaration(StmtVector &Stmts, bool OnlyStatement, + ParseStatementOrDeclaration(StmtVector &Stmts, AllowedContsructsKind Allowed, SourceLocation *TrailingElseLoc = nullptr); StmtResult ParseStatementOrDeclarationAfterAttributes( StmtVector &Stmts, - bool OnlyStatement, + AllowedContsructsKind Allowed, SourceLocation *TrailingElseLoc, ParsedAttributesWithRange &Attrs); StmtResult ParseExprStatement(); @@ -1674,7 +1687,8 @@ private: StmtResult ParseReturnStatement(); StmtResult ParseAsmStatement(bool &msAsm); StmtResult ParseMicrosoftAsmStatement(SourceLocation AsmLoc); - StmtResult ParsePragmaLoopHint(StmtVector &Stmts, bool OnlyStatement, + StmtResult ParsePragmaLoopHint(StmtVector &Stmts, + AllowedContsructsKind Allowed, SourceLocation *TrailingElseLoc, ParsedAttributesWithRange &Attrs); @@ -2439,11 +2453,13 @@ private: bool AllowScopeSpecifier); /// \brief Parses declarative or executable directive. /// - /// \param StandAloneAllowed true if allowed stand-alone directives, - /// false - otherwise + /// \param Allowed ACK_Any, if any directives are allowed, + /// ACK_StatementsOpenMPAnyExecutable - if any executable directives are + /// allowed, ACK_StatementsOpenMPNonStandalone - if only non-standalone + /// executable directives are allowed. /// StmtResult - ParseOpenMPDeclarativeOrExecutableDirective(bool StandAloneAllowed); + ParseOpenMPDeclarativeOrExecutableDirective(AllowedContsructsKind Allowed); /// \brief Parses clause of kind \a CKind for directive of a kind \a Kind. /// /// \param DKind Kind of current directive. diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index e9fdb707f277..064d37b2a02b 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -337,6 +337,7 @@ private: unsigned TypeAltiVecPixel : 1; unsigned TypeAltiVecBool : 1; unsigned TypeSpecOwned : 1; + unsigned TypeSpecPipe : 1; // type-qualifiers unsigned TypeQualifiers : 4; // Bitwise OR of TQ. @@ -385,6 +386,7 @@ private: SourceLocation FS_inlineLoc, FS_virtualLoc, FS_explicitLoc, FS_noreturnLoc; SourceLocation FS_forceinlineLoc; SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc, ConceptLoc; + SourceLocation TQ_pipeLoc; WrittenBuiltinSpecs writtenBS; void SaveWrittenBuiltinSpecs(); @@ -420,6 +422,7 @@ public: TypeAltiVecPixel(false), TypeAltiVecBool(false), TypeSpecOwned(false), + TypeSpecPipe(false), TypeQualifiers(TQ_unspecified), FS_inline_specified(false), FS_forceinline_specified(false), @@ -473,6 +476,7 @@ public: bool isTypeAltiVecBool() const { return TypeAltiVecBool; } bool isTypeSpecOwned() const { return TypeSpecOwned; } bool isTypeRep() const { return isTypeRep((TST) TypeSpecType); } + bool isTypeSpecPipe() const { return TypeSpecPipe; } ParsedType getRepAsType() const { assert(isTypeRep((TST) TypeSpecType) && "DeclSpec does not store a type"); @@ -532,6 +536,7 @@ public: SourceLocation getRestrictSpecLoc() const { return TQ_restrictLoc; } SourceLocation getVolatileSpecLoc() const { return TQ_volatileLoc; } SourceLocation getAtomicSpecLoc() const { return TQ_atomicLoc; } + SourceLocation getPipeLoc() const { return TQ_pipeLoc; } /// \brief Clear out all of the type qualifiers. void ClearTypeQualifiers() { @@ -540,6 +545,7 @@ public: TQ_restrictLoc = SourceLocation(); TQ_volatileLoc = SourceLocation(); TQ_atomicLoc = SourceLocation(); + TQ_pipeLoc = SourceLocation(); } // function-specifier @@ -643,6 +649,9 @@ public: bool SetTypeAltiVecBool(bool isAltiVecBool, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, const PrintingPolicy &Policy); + bool SetTypePipe(bool isPipe, SourceLocation Loc, + const char *&PrevSpec, unsigned &DiagID, + const PrintingPolicy &Policy); bool SetTypeSpecError(); void UpdateDeclRep(Decl *Rep) { assert(isDeclRep((TST) TypeSpecType)); @@ -1081,7 +1090,7 @@ typedef SmallVector<Token, 4> CachedTokens; /// This is intended to be a small value object. struct DeclaratorChunk { enum { - Pointer, Reference, Array, Function, BlockPointer, MemberPointer, Paren + Pointer, Reference, Array, Function, BlockPointer, MemberPointer, Paren, Pipe } Kind; /// Loc - The place where this type was defined. @@ -1409,6 +1418,13 @@ struct DeclaratorChunk { } }; + struct PipeTypeInfo : TypeInfoCommon { + /// The access writes. + unsigned AccessWrites : 3; + + void destroy() {} + }; + union { TypeInfoCommon Common; PointerTypeInfo Ptr; @@ -1417,6 +1433,7 @@ struct DeclaratorChunk { FunctionTypeInfo Fun; BlockPointerTypeInfo Cls; MemberPointerTypeInfo Mem; + PipeTypeInfo PipeInfo; }; void destroy() { @@ -1428,6 +1445,7 @@ struct DeclaratorChunk { case DeclaratorChunk::Array: return Arr.destroy(); case DeclaratorChunk::MemberPointer: return Mem.destroy(); case DeclaratorChunk::Paren: return; + case DeclaratorChunk::Pipe: return PipeInfo.destroy(); } } @@ -1526,6 +1544,17 @@ struct DeclaratorChunk { return I; } + /// \brief Return a DeclaratorChunk for a block. + static DeclaratorChunk getPipe(unsigned TypeQuals, + SourceLocation Loc) { + DeclaratorChunk I; + I.Kind = Pipe; + I.Loc = Loc; + I.Cls.TypeQuals = TypeQuals; + I.Cls.AttrList = 0; + return I; + } + static DeclaratorChunk getMemberPointer(const CXXScopeSpec &SS, unsigned TypeQuals, SourceLocation Loc) { @@ -2026,6 +2055,7 @@ public: case DeclaratorChunk::Array: case DeclaratorChunk::BlockPointer: case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: return false; } llvm_unreachable("Invalid type chunk"); diff --git a/include/clang/Sema/Lookup.h b/include/clang/Sema/Lookup.h index 87c40f0cf206..7efb19f57419 100644 --- a/include/clang/Sema/Lookup.h +++ b/include/clang/Sema/Lookup.h @@ -515,6 +515,7 @@ public: configure(); } + void dump(); void print(raw_ostream &); /// Suppress the diagnostics that would normally fire because of this diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h index 20958b057a4f..62437953b35b 100644 --- a/include/clang/Sema/Overload.h +++ b/include/clang/Sema/Overload.h @@ -570,8 +570,8 @@ namespace clang { /// This conversion candidate is not viable because its result /// type is not implicitly convertible to the desired type. ovl_fail_bad_final_conversion, - - /// This conversion function template specialization candidate is not + + /// This conversion function template specialization candidate is not /// viable because the final conversion was not an exact match. ovl_fail_final_conversion_not_exact, @@ -582,7 +582,10 @@ namespace clang { /// This candidate function was not viable because an enable_if /// attribute disabled it. - ovl_fail_enable_if + ovl_fail_enable_if, + + /// This candidate was not viable because its address could not be taken. + ovl_fail_addr_not_available }; /// OverloadCandidate - A single candidate in an overload set (C++ 13.3). diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 77d06f2affb7..ffe1ff3b950b 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1269,6 +1269,8 @@ public: SourceLocation Loc, DeclarationName Entity); QualType BuildParenType(QualType T); QualType BuildAtomicType(QualType T, SourceLocation Loc); + QualType BuildPipeType(QualType T, + SourceLocation Loc); TypeSourceInfo *GetTypeForDeclarator(Declarator &D, Scope *S); TypeSourceInfo *GetTypeForDeclaratorCast(Declarator &D, QualType FromTy); @@ -2548,7 +2550,8 @@ public: MultiExprArg Args, SourceLocation RParenLoc, Expr *ExecConfig, - bool AllowTypoCorrection=true); + bool AllowTypoCorrection=true, + bool CalleesAddressIsTaken=false); bool buildOverloadedCallSet(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, MultiExprArg Args, SourceLocation RParenLoc, @@ -7626,6 +7629,9 @@ public: void ActOnPragmaMSInitSeg(SourceLocation PragmaLocation, StringLiteral *SegmentName); + /// \brief Called on #pragma clang __debug dump II + void ActOnPragmaDump(Scope *S, SourceLocation Loc, IdentifierInfo *II); + /// ActOnPragmaDetectMismatch - Call on well-formed \#pragma detect_mismatch void ActOnPragmaDetectMismatch(StringRef Name, StringRef Value); @@ -8583,6 +8589,10 @@ public: bool CheckVectorCast(SourceRange R, QualType VectorTy, QualType Ty, CastKind &Kind); + /// \brief Prepare `SplattedExpr` for a vector splat operation, adding + /// implicit casts if necessary. + ExprResult prepareVectorSplat(QualType VectorTy, Expr *SplattedExpr); + // CheckExtVectorCast - check type constraints for extended vectors. // Since vectors are an extension, there are no C standard reference for this. // We allow casting between vectors and integer datatypes of the same size, diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 16bda6ea03c1..0dfb8cf37146 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -907,7 +907,9 @@ namespace clang { /// \brief A DecayedType record. TYPE_DECAYED = 41, /// \brief An AdjustedType record. - TYPE_ADJUSTED = 42 + TYPE_ADJUSTED = 42, + /// \brief A PipeType record. + TYPE_PIPE = 43 }; /// \brief The type IDs for special types constructed by semantic diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index ed345472fc7d..ef8c65341388 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -871,6 +871,7 @@ public: const FunctionDecl *Delete) override; void CompletedImplicitDefinition(const FunctionDecl *D) override; void StaticDataMemberInstantiated(const VarDecl *D) override; + void DefaultArgumentInstantiated(const ParmVarDecl *D) override; void FunctionDefinitionInstantiated(const FunctionDecl *D) override; void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, const ObjCInterfaceDecl *IFD) override; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index bb835c4c565e..43f6e5cda0dc 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -80,7 +80,7 @@ class MemRegion : public llvm::FoldingSetNode { public: enum Kind { // Memory spaces. - GenericMemSpaceRegionKind, + CodeSpaceRegionKind, StackLocalsSpaceRegionKind, StackArgumentsSpaceRegionKind, HeapSpaceRegionKind, @@ -89,29 +89,29 @@ public: GlobalInternalSpaceRegionKind, GlobalSystemSpaceRegionKind, GlobalImmutableSpaceRegionKind, - BEG_NON_STATIC_GLOBAL_MEMSPACES = GlobalInternalSpaceRegionKind, + BEGIN_NON_STATIC_GLOBAL_MEMSPACES = GlobalInternalSpaceRegionKind, END_NON_STATIC_GLOBAL_MEMSPACES = GlobalImmutableSpaceRegionKind, - BEG_GLOBAL_MEMSPACES = StaticGlobalSpaceRegionKind, + BEGIN_GLOBAL_MEMSPACES = StaticGlobalSpaceRegionKind, END_GLOBAL_MEMSPACES = GlobalImmutableSpaceRegionKind, - BEG_MEMSPACES = GenericMemSpaceRegionKind, + BEGIN_MEMSPACES = CodeSpaceRegionKind, END_MEMSPACES = GlobalImmutableSpaceRegionKind, // Untyped regions. SymbolicRegionKind, AllocaRegionKind, // Typed regions. - BEG_TYPED_REGIONS, - FunctionTextRegionKind = BEG_TYPED_REGIONS, - BlockTextRegionKind, + BEGIN_TYPED_REGIONS, + FunctionCodeRegionKind = BEGIN_TYPED_REGIONS, + BlockCodeRegionKind, BlockDataRegionKind, - BEG_TYPED_VALUE_REGIONS, - CompoundLiteralRegionKind = BEG_TYPED_VALUE_REGIONS, + BEGIN_TYPED_VALUE_REGIONS, + CompoundLiteralRegionKind = BEGIN_TYPED_VALUE_REGIONS, CXXThisRegionKind, StringRegionKind, ObjCStringRegionKind, ElementRegionKind, // Decl Regions. - BEG_DECL_REGIONS, - VarRegionKind = BEG_DECL_REGIONS, + BEGIN_DECL_REGIONS, + VarRegionKind = BEGIN_DECL_REGIONS, FieldRegionKind, ObjCIvarRegionKind, END_DECL_REGIONS = ObjCIvarRegionKind, @@ -193,12 +193,9 @@ public: /// for example, the set of global variables, the stack frame, etc. class MemSpaceRegion : public MemRegion { protected: - friend class MemRegionManager; - MemRegionManager *Mgr; - MemSpaceRegion(MemRegionManager *mgr, Kind k = GenericMemSpaceRegionKind) - : MemRegion(k), Mgr(mgr) { + MemSpaceRegion(MemRegionManager *mgr, Kind k) : MemRegion(k), Mgr(mgr) { assert(classof(this)); } @@ -211,10 +208,26 @@ public: static bool classof(const MemRegion *R) { Kind k = R->getKind(); - return k >= BEG_MEMSPACES && k <= END_MEMSPACES; + return k >= BEGIN_MEMSPACES && k <= END_MEMSPACES; } }; - + +/// CodeSpaceRegion - The memory space that holds the executable code of +/// functions and blocks. +class CodeSpaceRegion : public MemSpaceRegion { + friend class MemRegionManager; + + CodeSpaceRegion(MemRegionManager *mgr) + : MemSpaceRegion(mgr, CodeSpaceRegionKind) {} + +public: + void dumpToStream(raw_ostream &os) const override; + + static bool classof(const MemRegion *R) { + return R->getKind() == CodeSpaceRegionKind; + } +}; + class GlobalsSpaceRegion : public MemSpaceRegion { virtual void anchor(); protected: @@ -223,7 +236,7 @@ protected: public: static bool classof(const MemRegion *R) { Kind k = R->getKind(); - return k >= BEG_GLOBAL_MEMSPACES && k <= END_GLOBAL_MEMSPACES; + return k >= BEGIN_GLOBAL_MEMSPACES && k <= END_GLOBAL_MEMSPACES; } }; @@ -259,17 +272,15 @@ public: /// RegionStoreManager::invalidateRegions (instead of finding all the dependent /// globals, we invalidate the whole parent region). class NonStaticGlobalSpaceRegion : public GlobalsSpaceRegion { - friend class MemRegionManager; - protected: NonStaticGlobalSpaceRegion(MemRegionManager *mgr, Kind k) : GlobalsSpaceRegion(mgr, k) {} - + public: static bool classof(const MemRegion *R) { Kind k = R->getKind(); - return k >= BEG_NON_STATIC_GLOBAL_MEMSPACES && + return k >= BEGIN_NON_STATIC_GLOBAL_MEMSPACES && k <= END_NON_STATIC_GLOBAL_MEMSPACES; } }; @@ -357,7 +368,7 @@ public: return R->getKind() == UnknownSpaceRegionKind; } }; - + class StackSpaceRegion : public MemSpaceRegion { private: const StackFrameContext *SFC; @@ -368,18 +379,18 @@ protected: assert(classof(this)); } -public: +public: const StackFrameContext *getStackFrame() const { return SFC; } - + void Profile(llvm::FoldingSetNodeID &ID) const override; static bool classof(const MemRegion *R) { Kind k = R->getKind(); return k >= StackLocalsSpaceRegionKind && k <= StackArgumentsSpaceRegionKind; - } + } }; - + class StackLocalsSpaceRegion : public StackSpaceRegion { virtual void anchor(); friend class MemRegionManager; @@ -491,7 +502,7 @@ public: static bool classof(const MemRegion* R) { unsigned k = R->getKind(); - return k >= BEG_TYPED_REGIONS && k <= END_TYPED_REGIONS; + return k >= BEGIN_TYPED_REGIONS && k <= END_TYPED_REGIONS; } }; @@ -523,7 +534,7 @@ public: static bool classof(const MemRegion* R) { unsigned k = R->getKind(); - return k >= BEG_TYPED_VALUE_REGIONS && k <= END_TYPED_VALUE_REGIONS; + return k >= BEGIN_TYPED_VALUE_REGIONS && k <= END_TYPED_VALUE_REGIONS; } }; @@ -538,16 +549,16 @@ public: static bool classof(const MemRegion* R) { Kind k = R->getKind(); - return k >= FunctionTextRegionKind && k <= BlockTextRegionKind; + return k >= FunctionCodeRegionKind && k <= BlockCodeRegionKind; } }; -/// FunctionTextRegion - A region that represents code texts of function. -class FunctionTextRegion : public CodeTextRegion { +/// FunctionCodeRegion - A region that represents code texts of function. +class FunctionCodeRegion : public CodeTextRegion { const NamedDecl *FD; public: - FunctionTextRegion(const NamedDecl *fd, const MemRegion* sreg) - : CodeTextRegion(sreg, FunctionTextRegionKind), FD(fd) { + FunctionCodeRegion(const NamedDecl *fd, const MemRegion* sreg) + : CodeTextRegion(sreg, FunctionCodeRegionKind), FD(fd) { assert(isa<ObjCMethodDecl>(fd) || isa<FunctionDecl>(fd)); } @@ -577,27 +588,27 @@ public: const MemRegion*); static bool classof(const MemRegion* R) { - return R->getKind() == FunctionTextRegionKind; + return R->getKind() == FunctionCodeRegionKind; } }; -/// BlockTextRegion - A region that represents code texts of blocks (closures). -/// Blocks are represented with two kinds of regions. BlockTextRegions +/// BlockCodeRegion - A region that represents code texts of blocks (closures). +/// Blocks are represented with two kinds of regions. BlockCodeRegions /// represent the "code", while BlockDataRegions represent instances of blocks, /// which correspond to "code+data". The distinction is important, because /// like a closure a block captures the values of externally referenced /// variables. -class BlockTextRegion : public CodeTextRegion { +class BlockCodeRegion : public CodeTextRegion { friend class MemRegionManager; const BlockDecl *BD; AnalysisDeclContext *AC; CanQualType locTy; - BlockTextRegion(const BlockDecl *bd, CanQualType lTy, + BlockCodeRegion(const BlockDecl *bd, CanQualType lTy, AnalysisDeclContext *ac, const MemRegion* sreg) - : CodeTextRegion(sreg, BlockTextRegionKind), BD(bd), AC(ac), locTy(lTy) {} + : CodeTextRegion(sreg, BlockCodeRegionKind), BD(bd), AC(ac), locTy(lTy) {} public: QualType getLocationType() const override { @@ -619,32 +630,32 @@ public: const MemRegion*); static bool classof(const MemRegion* R) { - return R->getKind() == BlockTextRegionKind; + return R->getKind() == BlockCodeRegionKind; } }; /// BlockDataRegion - A region that represents a block instance. -/// Blocks are represented with two kinds of regions. BlockTextRegions +/// Blocks are represented with two kinds of regions. BlockCodeRegions /// represent the "code", while BlockDataRegions represent instances of blocks, /// which correspond to "code+data". The distinction is important, because /// like a closure a block captures the values of externally referenced /// variables. class BlockDataRegion : public TypedRegion { friend class MemRegionManager; - const BlockTextRegion *BC; + const BlockCodeRegion *BC; const LocationContext *LC; // Can be null */ unsigned BlockCount; void *ReferencedVars; void *OriginalVars; - BlockDataRegion(const BlockTextRegion *bc, const LocationContext *lc, + BlockDataRegion(const BlockCodeRegion *bc, const LocationContext *lc, unsigned count, const MemRegion *sreg) : TypedRegion(sreg, BlockDataRegionKind), BC(bc), LC(lc), BlockCount(count), ReferencedVars(nullptr), OriginalVars(nullptr) {} public: - const BlockTextRegion *getCodeRegion() const { return BC; } + const BlockCodeRegion *getCodeRegion() const { return BC; } const BlockDecl *getDecl() const { return BC->getDecl(); } @@ -691,7 +702,7 @@ public: void Profile(llvm::FoldingSetNodeID& ID) const override; - static void ProfileRegion(llvm::FoldingSetNodeID&, const BlockTextRegion *, + static void ProfileRegion(llvm::FoldingSetNodeID&, const BlockCodeRegion *, const LocationContext *, unsigned, const MemRegion *); @@ -856,7 +867,7 @@ public: static bool classof(const MemRegion* R) { unsigned k = R->getKind(); - return k >= BEG_DECL_REGIONS && k <= END_DECL_REGIONS; + return k >= BEGIN_DECL_REGIONS && k <= END_DECL_REGIONS; } }; @@ -1138,7 +1149,7 @@ class MemRegionManager { HeapSpaceRegion *heap; UnknownSpaceRegion *unknown; - MemSpaceRegion *code; + CodeSpaceRegion *code; public: MemRegionManager(ASTContext &c, llvm::BumpPtrAllocator &a) @@ -1174,9 +1185,9 @@ public: /// getUnknownRegion - Retrieve the memory region associated with unknown /// memory space. - const MemSpaceRegion *getUnknownRegion(); + const UnknownSpaceRegion *getUnknownRegion(); - const MemSpaceRegion *getCodeRegion(); + const CodeSpaceRegion *getCodeRegion(); /// getAllocaRegion - Retrieve a region associated with a call to alloca(). const AllocaRegion *getAllocaRegion(const Expr *Ex, unsigned Cnt, @@ -1262,8 +1273,8 @@ public: baseReg->isVirtual()); } - const FunctionTextRegion *getFunctionTextRegion(const NamedDecl *FD); - const BlockTextRegion *getBlockTextRegion(const BlockDecl *BD, + const FunctionCodeRegion *getFunctionCodeRegion(const NamedDecl *FD); + const BlockCodeRegion *getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy, AnalysisDeclContext *AC); @@ -1271,7 +1282,7 @@ public: /// of a block. Unlike many other MemRegions, the LocationContext* /// argument is allowed to be NULL for cases where we have no known /// context. - const BlockDataRegion *getBlockDataRegion(const BlockTextRegion *bc, + const BlockDataRegion *getBlockDataRegion(const BlockCodeRegion *bc, const LocationContext *lc, unsigned blockCount); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index a68d3410a87b..3c47114e2de2 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -83,7 +83,11 @@ public: } SVal evalCast(SVal val, QualType castTy, QualType originalType); - + + // Handles casts of type CK_IntegralCast. + SVal evalIntegralCast(ProgramStateRef state, SVal val, QualType castTy, + QualType originalType); + virtual SVal evalMinus(NonLoc val) = 0; virtual SVal evalComplement(NonLoc val) = 0; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index 642e11af0e6c..d64425412c89 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -45,8 +45,8 @@ class SVal { public: enum BaseKind { // The enumerators must be representable using 2 bits. - UndefinedKind = 0, // for subclass UndefinedVal (an uninitialized value) - UnknownKind = 1, // for subclass UnknownVal (a void value) + UndefinedValKind = 0, // for subclass UndefinedVal (an uninitialized value) + UnknownValKind = 1, // for subclass UnknownVal (a void value) LocKind = 2, // for subclass Loc (an L-value) NonLocKind = 3 // for subclass NonLoc (an R-value that's not // an L-value) @@ -115,19 +115,19 @@ public: } inline bool isUnknown() const { - return getRawKind() == UnknownKind; + return getRawKind() == UnknownValKind; } inline bool isUndef() const { - return getRawKind() == UndefinedKind; + return getRawKind() == UndefinedValKind; } inline bool isUnknownOrUndef() const { - return getRawKind() <= UnknownKind; + return getRawKind() <= UnknownValKind; } inline bool isValid() const { - return getRawKind() > UnknownKind; + return getRawKind() > UnknownValKind; } bool isConstant() const; @@ -190,12 +190,12 @@ public: class UndefinedVal : public SVal { public: - UndefinedVal() : SVal(UndefinedKind) {} + UndefinedVal() : SVal(UndefinedValKind) {} private: friend class SVal; static bool isKind(const SVal& V) { - return V.getBaseKind() == UndefinedKind; + return V.getBaseKind() == UndefinedValKind; } }; @@ -223,12 +223,12 @@ private: class UnknownVal : public DefinedOrUnknownSVal { public: - explicit UnknownVal() : DefinedOrUnknownSVal(UnknownKind) {} + explicit UnknownVal() : DefinedOrUnknownSVal(UnknownValKind) {} private: friend class SVal; static bool isKind(const SVal &V) { - return V.getBaseKind() == UnknownKind; + return V.getBaseKind() == UnknownValKind; } }; @@ -465,7 +465,7 @@ private: namespace loc { -enum Kind { GotoLabelKind, MemRegionKind, ConcreteIntKind }; +enum Kind { GotoLabelKind, MemRegionValKind, ConcreteIntKind }; class GotoLabel : public Loc { public: @@ -490,7 +490,7 @@ private: class MemRegionVal : public Loc { public: - explicit MemRegionVal(const MemRegion* r) : Loc(MemRegionKind, r) {} + explicit MemRegionVal(const MemRegion* r) : Loc(MemRegionValKind, r) {} /// \brief Get the underlining region. const MemRegion* getRegion() const { @@ -518,11 +518,11 @@ private: MemRegionVal() {} static bool isKind(const SVal& V) { return V.getBaseKind() == LocKind && - V.getSubKind() == MemRegionKind; + V.getSubKind() == MemRegionValKind; } static bool isKind(const Loc& V) { - return V.getSubKind() == MemRegionKind; + return V.getSubKind() == MemRegionValKind; } }; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index 9dbfab24b417..77d12e5ba666 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -42,14 +42,22 @@ namespace ento { class SymExpr : public llvm::FoldingSetNode { virtual void anchor(); public: - enum Kind { RegionValueKind, ConjuredKind, DerivedKind, ExtentKind, - MetadataKind, - BEGIN_SYMBOLS = RegionValueKind, - END_SYMBOLS = MetadataKind, - SymIntKind, IntSymKind, SymSymKind, - BEGIN_BINARYSYMEXPRS = SymIntKind, - END_BINARYSYMEXPRS = SymSymKind, - CastSymbolKind }; + enum Kind { + SymbolRegionValueKind, + SymbolConjuredKind, + SymbolDerivedKind, + SymbolExtentKind, + SymbolMetadataKind, + BEGIN_SYMBOLS = SymbolRegionValueKind, + END_SYMBOLS = SymbolMetadataKind, + SymIntExprKind, + IntSymExprKind, + SymSymExprKind, + BEGIN_BINARYSYMEXPRS = SymIntExprKind, + END_BINARYSYMEXPRS = SymSymExprKind, + SymbolCastKind + }; + private: Kind K; @@ -126,12 +134,12 @@ class SymbolRegionValue : public SymbolData { public: SymbolRegionValue(SymbolID sym, const TypedValueRegion *r) - : SymbolData(RegionValueKind, sym), R(r) {} + : SymbolData(SymbolRegionValueKind, sym), R(r) {} const TypedValueRegion* getRegion() const { return R; } static void Profile(llvm::FoldingSetNodeID& profile, const TypedValueRegion* R) { - profile.AddInteger((unsigned) RegionValueKind); + profile.AddInteger((unsigned) SymbolRegionValueKind); profile.AddPointer(R); } @@ -145,7 +153,7 @@ public: // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { - return SE->getKind() == RegionValueKind; + return SE->getKind() == SymbolRegionValueKind; } }; @@ -160,11 +168,9 @@ class SymbolConjured : public SymbolData { public: SymbolConjured(SymbolID sym, const Stmt *s, const LocationContext *lctx, - QualType t, unsigned count, - const void *symbolTag) - : SymbolData(ConjuredKind, sym), S(s), T(t), Count(count), - LCtx(lctx), - SymbolTag(symbolTag) {} + QualType t, unsigned count, const void *symbolTag) + : SymbolData(SymbolConjuredKind, sym), S(s), T(t), Count(count), + LCtx(lctx), SymbolTag(symbolTag) {} const Stmt *getStmt() const { return S; } unsigned getCount() const { return Count; } @@ -177,7 +183,7 @@ public: static void Profile(llvm::FoldingSetNodeID& profile, const Stmt *S, QualType T, unsigned Count, const LocationContext *LCtx, const void *SymbolTag) { - profile.AddInteger((unsigned) ConjuredKind); + profile.AddInteger((unsigned) SymbolConjuredKind); profile.AddPointer(S); profile.AddPointer(LCtx); profile.Add(T); @@ -191,7 +197,7 @@ public: // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { - return SE->getKind() == ConjuredKind; + return SE->getKind() == SymbolConjuredKind; } }; @@ -203,7 +209,7 @@ class SymbolDerived : public SymbolData { public: SymbolDerived(SymbolID sym, SymbolRef parent, const TypedValueRegion *r) - : SymbolData(DerivedKind, sym), parentSymbol(parent), R(r) {} + : SymbolData(SymbolDerivedKind, sym), parentSymbol(parent), R(r) {} SymbolRef getParentSymbol() const { return parentSymbol; } const TypedValueRegion *getRegion() const { return R; } @@ -214,7 +220,7 @@ public: static void Profile(llvm::FoldingSetNodeID& profile, SymbolRef parent, const TypedValueRegion *r) { - profile.AddInteger((unsigned) DerivedKind); + profile.AddInteger((unsigned) SymbolDerivedKind); profile.AddPointer(r); profile.AddPointer(parent); } @@ -225,7 +231,7 @@ public: // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { - return SE->getKind() == DerivedKind; + return SE->getKind() == SymbolDerivedKind; } }; @@ -237,7 +243,7 @@ class SymbolExtent : public SymbolData { public: SymbolExtent(SymbolID sym, const SubRegion *r) - : SymbolData(ExtentKind, sym), R(r) {} + : SymbolData(SymbolExtentKind, sym), R(r) {} const SubRegion *getRegion() const { return R; } @@ -246,7 +252,7 @@ public: void dumpToStream(raw_ostream &os) const override; static void Profile(llvm::FoldingSetNodeID& profile, const SubRegion *R) { - profile.AddInteger((unsigned) ExtentKind); + profile.AddInteger((unsigned) SymbolExtentKind); profile.AddPointer(R); } @@ -256,7 +262,7 @@ public: // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { - return SE->getKind() == ExtentKind; + return SE->getKind() == SymbolExtentKind; } }; @@ -273,7 +279,7 @@ class SymbolMetadata : public SymbolData { public: SymbolMetadata(SymbolID sym, const MemRegion* r, const Stmt *s, QualType t, unsigned count, const void *tag) - : SymbolData(MetadataKind, sym), R(r), S(s), T(t), Count(count), Tag(tag) {} + : SymbolData(SymbolMetadataKind, sym), R(r), S(s), T(t), Count(count), Tag(tag) {} const MemRegion *getRegion() const { return R; } const Stmt *getStmt() const { return S; } @@ -287,7 +293,7 @@ public: static void Profile(llvm::FoldingSetNodeID& profile, const MemRegion *R, const Stmt *S, QualType T, unsigned Count, const void *Tag) { - profile.AddInteger((unsigned) MetadataKind); + profile.AddInteger((unsigned) SymbolMetadataKind); profile.AddPointer(R); profile.AddPointer(S); profile.Add(T); @@ -301,7 +307,7 @@ public: // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { - return SE->getKind() == MetadataKind; + return SE->getKind() == SymbolMetadataKind; } }; @@ -315,7 +321,7 @@ class SymbolCast : public SymExpr { public: SymbolCast(const SymExpr *In, QualType From, QualType To) : - SymExpr(CastSymbolKind), Operand(In), FromTy(From), ToTy(To) { } + SymExpr(SymbolCastKind), Operand(In), FromTy(From), ToTy(To) { } QualType getType() const override { return ToTy; } @@ -325,7 +331,7 @@ public: static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *In, QualType From, QualType To) { - ID.AddInteger((unsigned) CastSymbolKind); + ID.AddInteger((unsigned) SymbolCastKind); ID.AddPointer(In); ID.Add(From); ID.Add(To); @@ -337,7 +343,7 @@ public: // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { - return SE->getKind() == CastSymbolKind; + return SE->getKind() == SymbolCastKind; } }; @@ -372,7 +378,7 @@ class SymIntExpr : public BinarySymExpr { public: SymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const llvm::APSInt& rhs, QualType t) - : BinarySymExpr(SymIntKind, op, t), LHS(lhs), RHS(rhs) {} + : BinarySymExpr(SymIntExprKind, op, t), LHS(lhs), RHS(rhs) {} void dumpToStream(raw_ostream &os) const override; @@ -382,7 +388,7 @@ public: static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, BinaryOperator::Opcode op, const llvm::APSInt& rhs, QualType t) { - ID.AddInteger((unsigned) SymIntKind); + ID.AddInteger((unsigned) SymIntExprKind); ID.AddPointer(lhs); ID.AddInteger(op); ID.AddPointer(&rhs); @@ -395,7 +401,7 @@ public: // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { - return SE->getKind() == SymIntKind; + return SE->getKind() == SymIntExprKind; } }; @@ -407,7 +413,7 @@ class IntSymExpr : public BinarySymExpr { public: IntSymExpr(const llvm::APSInt& lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) - : BinarySymExpr(IntSymKind, op, t), LHS(lhs), RHS(rhs) {} + : BinarySymExpr(IntSymExprKind, op, t), LHS(lhs), RHS(rhs) {} void dumpToStream(raw_ostream &os) const override; @@ -417,7 +423,7 @@ public: static void Profile(llvm::FoldingSetNodeID& ID, const llvm::APSInt& lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) { - ID.AddInteger((unsigned) IntSymKind); + ID.AddInteger((unsigned) IntSymExprKind); ID.AddPointer(&lhs); ID.AddInteger(op); ID.AddPointer(rhs); @@ -430,7 +436,7 @@ public: // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { - return SE->getKind() == IntSymKind; + return SE->getKind() == IntSymExprKind; } }; @@ -442,7 +448,7 @@ class SymSymExpr : public BinarySymExpr { public: SymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) - : BinarySymExpr(SymSymKind, op, t), LHS(lhs), RHS(rhs) {} + : BinarySymExpr(SymSymExprKind, op, t), LHS(lhs), RHS(rhs) {} const SymExpr *getLHS() const { return LHS; } const SymExpr *getRHS() const { return RHS; } @@ -451,7 +457,7 @@ public: static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) { - ID.AddInteger((unsigned) SymSymKind); + ID.AddInteger((unsigned) SymSymExprKind); ID.AddPointer(lhs); ID.AddInteger(op); ID.AddPointer(rhs); @@ -464,7 +470,7 @@ public: // Implement isa<T> support. static inline bool classof(const SymExpr *SE) { - return SE->getKind() == SymSymKind; + return SE->getKind() == SymSymExprKind; } }; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index d4abbe47cafb..64386967b220 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1836,6 +1836,13 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Align = static_cast<unsigned>(Width); } } + break; + + case Type::Pipe: { + TypeInfo Info = getTypeInfo(cast<PipeType>(T)->getElementType()); + Width = Info.Width; + Align = Info.Align; + } } @@ -2663,6 +2670,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const { case Type::FunctionProto: case Type::BlockPointer: case Type::MemberPointer: + case Type::Pipe: return type; // These types can be variably-modified. All these modifications @@ -3117,6 +3125,32 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef<QualType> ArgArray, return QualType(FTP, 0); } +/// Return pipe type for the specified type. +QualType ASTContext::getPipeType(QualType T) const { + llvm::FoldingSetNodeID ID; + PipeType::Profile(ID, T); + + void *InsertPos = 0; + if (PipeType *PT = PipeTypes.FindNodeOrInsertPos(ID, InsertPos)) + return QualType(PT, 0); + + // If the pipe element type isn't canonical, this won't be a canonical type + // either, so fill in the canonical type field. + QualType Canonical; + if (!T.isCanonical()) { + Canonical = getPipeType(getCanonicalType(T)); + + // Get the new insert position for the node we care about. + PipeType *NewIP = PipeTypes.FindNodeOrInsertPos(ID, InsertPos); + assert(!NewIP && "Shouldn't be in the map!"); + (void)NewIP; + } + PipeType *New = new (*this, TypeAlignment) PipeType(T, Canonical); + Types.push_back(New); + PipeTypes.InsertNode(New, InsertPos); + return QualType(New, 0); +} + #ifndef NDEBUG static bool NeedsInjectedClassNameType(const RecordDecl *D) { if (!isa<CXXRecordDecl>(D)) return false; @@ -5857,6 +5891,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string& S, case Type::Auto: return; + case Type::Pipe: #define ABSTRACT_TYPE(KIND, BASE) #define TYPE(KIND, BASE) #define DEPENDENT_TYPE(KIND, BASE) \ @@ -7792,6 +7827,24 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, return QualType(); } + case Type::Pipe: + { + // Merge two pointer types, while trying to preserve typedef info + QualType LHSValue = LHS->getAs<PipeType>()->getElementType(); + QualType RHSValue = RHS->getAs<PipeType>()->getElementType(); + if (Unqualified) { + LHSValue = LHSValue.getUnqualifiedType(); + RHSValue = RHSValue.getUnqualifiedType(); + } + QualType ResultType = mergeTypes(LHSValue, RHSValue, false, + Unqualified); + if (ResultType.isNull()) return QualType(); + if (getCanonicalType(LHSValue) == getCanonicalType(ResultType)) + return LHS; + if (getCanonicalType(RHSValue) == getCanonicalType(ResultType)) + return RHS; + return getPipeType(ResultType); + } } llvm_unreachable("Invalid Type::Class!"); diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index e7fee0316b69..4622a75ac2c6 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1055,6 +1055,7 @@ void ASTDumper::VisitTypedefDecl(const TypedefDecl *D) { dumpType(D->getUnderlyingType()); if (D->isModulePrivate()) OS << " __module_private__"; + dumpTypeAsChild(D->getUnderlyingType()); } void ASTDumper::VisitEnumDecl(const EnumDecl *D) { @@ -1226,6 +1227,7 @@ void ASTDumper::VisitNamespaceAliasDecl(const NamespaceAliasDecl *D) { void ASTDumper::VisitTypeAliasDecl(const TypeAliasDecl *D) { dumpName(D); dumpType(D->getUnderlyingType()); + dumpTypeAsChild(D->getUnderlyingType()); } void ASTDumper::VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D) { @@ -1419,6 +1421,8 @@ void ASTDumper::VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) void ASTDumper::VisitUsingShadowDecl(const UsingShadowDecl *D) { OS << ' '; dumpBareDeclRef(D->getTargetDecl()); + if (auto *TD = dyn_cast<TypeDecl>(D->getUnderlyingDecl())) + dumpTypeAsChild(TD->getTypeForDecl()); } void ASTDumper::VisitLinkageSpecDecl(const LinkageSpecDecl *D) { diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 359db1ba81b3..916f1081798d 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -878,6 +878,14 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, break; } + case Type::Pipe: { + if (!IsStructurallyEquivalent(Context, + cast<PipeType>(T1)->getElementType(), + cast<PipeType>(T2)->getElementType())) + return false; + break; + } + } // end switch return true; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 42bebc543e3e..427ca5efcd69 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1184,7 +1184,7 @@ static LinkageInfo getLVForLocalDecl(const NamedDecl *D, return LinkageInfo::none(); const Decl *OuterD = getOutermostFuncOrBlockContext(D); - if (!OuterD) + if (!OuterD || OuterD->isInvalidDecl()) return LinkageInfo::none(); LinkageInfo LV; @@ -4024,16 +4024,26 @@ EnumConstantDecl::CreateDeserialized(ASTContext &C, unsigned ID) { void IndirectFieldDecl::anchor() { } +IndirectFieldDecl::IndirectFieldDecl(ASTContext &C, DeclContext *DC, + SourceLocation L, DeclarationName N, + QualType T, NamedDecl **CH, unsigned CHS) + : ValueDecl(IndirectField, DC, L, N, T), Chaining(CH), ChainingSize(CHS) { + // In C++, indirect field declarations conflict with tag declarations in the + // same scope, so add them to IDNS_Tag so that tag redeclaration finds them. + if (C.getLangOpts().CPlusPlus) + IdentifierNamespace |= IDNS_Tag; +} + IndirectFieldDecl * IndirectFieldDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, QualType T, NamedDecl **CH, unsigned CHS) { - return new (C, DC) IndirectFieldDecl(DC, L, Id, T, CH, CHS); + return new (C, DC) IndirectFieldDecl(C, DC, L, Id, T, CH, CHS); } IndirectFieldDecl *IndirectFieldDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) IndirectFieldDecl(nullptr, SourceLocation(), + return new (C, ID) IndirectFieldDecl(C, nullptr, SourceLocation(), DeclarationName(), QualType(), nullptr, 0); } diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 16394e865eb1..72587e388e47 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -569,7 +569,6 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case Var: case ImplicitParam: case ParmVar: - case NonTypeTemplateParm: case ObjCMethod: case ObjCProperty: case MSProperty: @@ -579,6 +578,12 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case IndirectField: return IDNS_Ordinary | IDNS_Member; + case NonTypeTemplateParm: + // Non-type template parameters are not found by lookups that ignore + // non-types, but they are found by redeclaration lookups for tag types, + // so we include them in the tag namespace. + return IDNS_Ordinary | IDNS_Tag; + case ObjCCompatibleAlias: case ObjCInterface: return IDNS_Ordinary | IDNS_Type; diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index f9757b209294..52f34df43565 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1553,6 +1553,7 @@ bool CastExpr::CastConsistency() const { case CK_ToVoid: case CK_VectorSplat: case CK_IntegralCast: + case CK_BooleanToSignedIntegral: case CK_IntegralToFloating: case CK_FloatingToIntegral: case CK_FloatingCast: @@ -1646,6 +1647,8 @@ const char *CastExpr::getCastKindName() const { return "VectorSplat"; case CK_IntegralCast: return "IntegralCast"; + case CK_BooleanToSignedIntegral: + return "BooleanToSignedIntegral"; case CK_IntegralToBoolean: return "IntegralToBoolean"; case CK_IntegralToFloating: diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index d35efcb101b5..ea983340a293 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -763,14 +763,6 @@ const IdentifierInfo *UserDefinedLiteral::getUDSuffix() const { return cast<FunctionDecl>(getCalleeDecl())->getLiteralIdentifier(); } -CXXDefaultArgExpr * -CXXDefaultArgExpr::Create(const ASTContext &C, SourceLocation Loc, - ParmVarDecl *Param, Expr *SubExpr) { - void *Mem = C.Allocate(totalSizeToAlloc<Expr *>(1)); - return new (Mem) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, - SubExpr); -} - CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &C, SourceLocation Loc, FieldDecl *Field, QualType T) : Expr(CXXDefaultInitExprClass, T.getNonLValueExprType(C), diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index c4c4398c3622..fa652ba1b0ab 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -7781,12 +7781,16 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_PointerToBoolean: case CK_IntegralToBoolean: case CK_FloatingToBoolean: + case CK_BooleanToSignedIntegral: case CK_FloatingComplexToBoolean: case CK_IntegralComplexToBoolean: { bool BoolResult; if (!EvaluateAsBooleanCondition(SubExpr, BoolResult, Info)) return false; - return Success(BoolResult, E); + uint64_t IntResult = BoolResult; + if (BoolResult && E->getCastKind() == CK_BooleanToSignedIntegral) + IntResult = (uint64_t)-1; + return Success(IntResult, E); } case CK_IntegralCast: { @@ -8223,6 +8227,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_ToVoid: case CK_VectorSplat: case CK_IntegralCast: + case CK_BooleanToSignedIntegral: case CK_IntegralToBoolean: case CK_IntegralToFloating: case CK_FloatingToIntegral: diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 8018188a6b24..3f6b682f238f 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -1509,6 +1509,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty, case Type::ObjCInterface: case Type::ObjCObjectPointer: case Type::Atomic: + case Type::Pipe: llvm_unreachable("type is illegal as a nested name specifier"); case Type::SubstTemplateTypeParmPack: @@ -2682,6 +2683,13 @@ void CXXNameMangler::mangleType(const AtomicType *T) { mangleType(T->getValueType()); } +void CXXNameMangler::mangleType(const PipeType *T) { + // Pipe type mangling rules are described in SPIR 2.0 specification + // A.1 Data types and A.3 Summary of changes + // <type> ::= 8ocl_pipe + Out << "8ocl_pipe"; +} + void CXXNameMangler::mangleIntegerLiteral(QualType T, const llvm::APSInt &Value) { // <expr-primary> ::= L <type> <value number> E # integer literal diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index d45232b6deb0..4a45f9e4051f 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -2428,6 +2428,15 @@ void MicrosoftCXXNameMangler::mangleType(const AtomicType *T, Qualifiers, mangleArtificalTagType(TTK_Struct, TemplateMangling, {"__clang"}); } +void MicrosoftCXXNameMangler::mangleType(const PipeType *T, Qualifiers, + SourceRange Range) { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error, + "cannot mangle this OpenCL pipe type yet"); + Diags.Report(Range.getBegin(), DiagID) + << Range; +} + void MicrosoftMangleContextImpl::mangleCXXName(const NamedDecl *D, raw_ostream &Out) { assert((isa<FunctionDecl>(D) || isa<VarDecl>(D)) && diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index bc3c2a831c47..bc5ae0ffc469 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -1552,7 +1552,8 @@ void ItaniumRecordLayoutBuilder::LayoutBitField(const FieldDecl *D) { FieldAlign = 1; // But, if there's an 'aligned' attribute on the field, honor that. - if (unsigned ExplicitFieldAlign = D->getMaxAlignment()) { + unsigned ExplicitFieldAlign = D->getMaxAlignment(); + if (ExplicitFieldAlign) { FieldAlign = std::max(FieldAlign, ExplicitFieldAlign); UnpackedFieldAlign = std::max(UnpackedFieldAlign, ExplicitFieldAlign); } @@ -1601,6 +1602,10 @@ void ItaniumRecordLayoutBuilder::LayoutBitField(const FieldDecl *D) { (AllowPadding && (FieldOffset & (FieldAlign-1)) + FieldSize > TypeSize)) { FieldOffset = llvm::RoundUpToAlignment(FieldOffset, FieldAlign); + } else if (ExplicitFieldAlign) { + // TODO: figure it out what needs to be done on targets that don't honor + // bit-field type alignment like ARM APCS ABI. + FieldOffset = llvm::RoundUpToAlignment(FieldOffset, ExplicitFieldAlign); } // Repeat the computation for diagnostic purposes. @@ -1609,6 +1614,9 @@ void ItaniumRecordLayoutBuilder::LayoutBitField(const FieldDecl *D) { (UnpackedFieldOffset & (UnpackedFieldAlign-1)) + FieldSize > TypeSize)) UnpackedFieldOffset = llvm::RoundUpToAlignment(UnpackedFieldOffset, UnpackedFieldAlign); + else if (ExplicitFieldAlign) + UnpackedFieldOffset = llvm::RoundUpToAlignment(UnpackedFieldOffset, + ExplicitFieldAlign); } // If we're using external layout, give the external layout a chance diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index e55b2fc19a1a..69f52f52b669 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1165,6 +1165,7 @@ void StmtPrinter::VisitCharacterLiteral(CharacterLiteral *Node) { switch (Node->getKind()) { case CharacterLiteral::Ascii: break; // no prefix. case CharacterLiteral::Wide: OS << 'L'; break; + case CharacterLiteral::UTF8: OS << "u8"; break; case CharacterLiteral::UTF16: OS << 'u'; break; case CharacterLiteral::UTF32: OS << 'U'; break; } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 7dd38cba229b..b467dac66b57 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2614,7 +2614,7 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { case OCLQueue: return "queue_t"; case OCLNDRange: - return "event_t"; + return "ndrange_t"; case OCLReserveID: return "reserve_id_t"; case OMPArraySection: @@ -3361,6 +3361,8 @@ static CachedProperties computeCachedProperties(const Type *T) { return Cache::get(cast<ObjCObjectPointerType>(T)->getPointeeType()); case Type::Atomic: return Cache::get(cast<AtomicType>(T)->getValueType()); + case Type::Pipe: + return Cache::get(cast<PipeType>(T)->getElementType()); } llvm_unreachable("unhandled type class"); @@ -3443,6 +3445,8 @@ static LinkageInfo computeLinkageInfo(const Type *T) { return computeLinkageInfo(cast<ObjCObjectPointerType>(T)->getPointeeType()); case Type::Atomic: return computeLinkageInfo(cast<AtomicType>(T)->getValueType()); + case Type::Pipe: + return computeLinkageInfo(cast<PipeType>(T)->getElementType()); } llvm_unreachable("unhandled type class"); @@ -3601,6 +3605,7 @@ bool Type::canHaveNullability() const { case Type::ObjCObject: case Type::ObjCInterface: case Type::Atomic: + case Type::Pipe: return false; } llvm_unreachable("bad type kind!"); diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 4617e1d3803f..b202523bdaf3 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -193,6 +193,7 @@ bool TypePrinter::canPrefixQualifiers(const Type *T, case Type::ObjCObject: case Type::ObjCInterface: case Type::Atomic: + case Type::Pipe: CanPrefixQualifiers = true; break; @@ -859,6 +860,15 @@ void TypePrinter::printAtomicBefore(const AtomicType *T, raw_ostream &OS) { } void TypePrinter::printAtomicAfter(const AtomicType *T, raw_ostream &OS) { } +void TypePrinter::printPipeBefore(const PipeType *T, raw_ostream &OS) { + IncludeStrongLifetimeRAII Strong(Policy); + + OS << "pipe"; + spaceBeforePlaceHolder(OS); +} + +void TypePrinter::printPipeAfter(const PipeType *T, raw_ostream &OS) { +} /// Appends the given scope to the end of a string. void TypePrinter::AppendScope(DeclContext *DC, raw_ostream &OS) { if (DC->isTranslationUnit()) return; diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 9ce525709cf4..1bc6c51b9b85 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -223,7 +223,24 @@ protected: public: DarwinTargetInfo(const llvm::Triple &Triple) : OSTargetInfo<Target>(Triple) { - this->TLSSupported = Triple.isMacOSX() && !Triple.isMacOSXVersionLT(10, 7); + // By default, no TLS, and we whitelist permitted architecture/OS + // combinations. + this->TLSSupported = false; + + if (Triple.isMacOSX()) + this->TLSSupported = !Triple.isMacOSXVersionLT(10, 7); + else if (Triple.isiOS()) { + // 64-bit iOS supported it from 8 onwards, 32-bit from 9 onwards. + if (Triple.getArch() == llvm::Triple::x86_64 || + Triple.getArch() == llvm::Triple::aarch64) + this->TLSSupported = !Triple.isOSVersionLT(8); + else if (Triple.getArch() == llvm::Triple::x86 || + Triple.getArch() == llvm::Triple::arm || + Triple.getArch() == llvm::Triple::thumb) + this->TLSSupported = !Triple.isOSVersionLT(9); + } else if (Triple.isWatchOS()) + this->TLSSupported = !Triple.isOSVersionLT(2); + this->MCountName = "\01mcount"; } @@ -7281,7 +7298,7 @@ public: explicit WebAssembly32TargetInfo(const llvm::Triple &T) : WebAssemblyTargetInfo(T) { MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 32; - DataLayoutString = "e-p:32:32-i64:64-n32:64-S128"; + DataLayoutString = "e-m:e-p:32:32-i64:64-n32:64-S128"; } protected: @@ -7299,7 +7316,7 @@ public: LongAlign = LongWidth = 64; PointerAlign = PointerWidth = 64; MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64; - DataLayoutString = "e-p:64:64-i64:64-n32:64-S128"; + DataLayoutString = "e-m:e-p:64:64-i64:64-n32:64-S128"; } protected: diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp index cf5a8d681eac..6977f400287f 100644 --- a/lib/Basic/VirtualFileSystem.cpp +++ b/lib/Basic/VirtualFileSystem.cpp @@ -658,6 +658,23 @@ directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir, EC = make_error_code(llvm::errc::not_a_directory); return directory_iterator(std::make_shared<InMemoryDirIterator>()); } + +std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) { + SmallString<128> Path; + P.toVector(Path); + + // Fix up relative paths. This just prepends the current working directory. + std::error_code EC = makeAbsolute(Path); + assert(!EC); + (void)EC; + + if (useNormalizedPaths()) + llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + + if (!Path.empty()) + WorkingDirectory = Path.str(); + return std::error_code(); +} } } diff --git a/lib/CodeGen/BackendUtil.cpp b/lib/CodeGen/BackendUtil.cpp index 7032d00386f3..6d746c25eed1 100644 --- a/lib/CodeGen/BackendUtil.cpp +++ b/lib/CodeGen/BackendUtil.cpp @@ -28,6 +28,7 @@ #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "llvm/MC/SubtargetFeature.h" +#include "llvm/Object/FunctionIndexObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/TargetRegistry.h" @@ -54,7 +55,6 @@ class EmitAssemblyHelper { const clang::TargetOptions &TargetOpts; const LangOptions &LangOpts; Module *TheModule; - std::unique_ptr<FunctionInfoIndex> FunctionIndex; Timer CodeGenerationTime; @@ -97,7 +97,7 @@ private: return PerFunctionPasses; } - void CreatePasses(); + void CreatePasses(FunctionInfoIndex *FunctionIndex); /// Generates the TargetMachine. /// Returns Null if it is unable to create the target machine. @@ -117,12 +117,11 @@ private: public: EmitAssemblyHelper(DiagnosticsEngine &_Diags, const CodeGenOptions &CGOpts, const clang::TargetOptions &TOpts, - const LangOptions &LOpts, Module *M, - std::unique_ptr<FunctionInfoIndex> Index) + const LangOptions &LOpts, Module *M) : Diags(_Diags), CodeGenOpts(CGOpts), TargetOpts(TOpts), LangOpts(LOpts), - TheModule(M), FunctionIndex(std::move(Index)), - CodeGenerationTime("Code Generation Time"), CodeGenPasses(nullptr), - PerModulePasses(nullptr), PerFunctionPasses(nullptr) {} + TheModule(M), CodeGenerationTime("Code Generation Time"), + CodeGenPasses(nullptr), PerModulePasses(nullptr), + PerFunctionPasses(nullptr) {} ~EmitAssemblyHelper() { delete CodeGenPasses; @@ -278,7 +277,7 @@ static void addSymbolRewriterPass(const CodeGenOptions &Opts, MPM->add(createRewriteSymbolsPass(DL)); } -void EmitAssemblyHelper::CreatePasses() { +void EmitAssemblyHelper::CreatePasses(FunctionInfoIndex *FunctionIndex) { if (CodeGenOpts.DisableLLVMPasses) return; @@ -332,9 +331,8 @@ void EmitAssemblyHelper::CreatePasses() { // If we are performing a ThinLTO importing compile, invoke the LTO // pipeline and pass down the in-memory function index. - if (!CodeGenOpts.ThinLTOIndexFile.empty()) { - assert(FunctionIndex && "Expected non-empty function index"); - PMBuilder.FunctionIndex = FunctionIndex.get(); + if (FunctionIndex) { + PMBuilder.FunctionIndex = FunctionIndex; PMBuilder.populateLTOPassManager(*MPM); return; } @@ -642,7 +640,28 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, return; if (TM) TheModule->setDataLayout(TM->createDataLayout()); - CreatePasses(); + + // If we are performing a ThinLTO importing compile, load the function + // index into memory and pass it into CreatePasses, which will add it + // to the PassManagerBuilder and invoke LTO passes. + std::unique_ptr<FunctionInfoIndex> FunctionIndex; + if (!CodeGenOpts.ThinLTOIndexFile.empty()) { + ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr = + llvm::getFunctionIndexForFile(CodeGenOpts.ThinLTOIndexFile, + [&](const DiagnosticInfo &DI) { + TheModule->getContext().diagnose(DI); + }); + if (std::error_code EC = IndexOrErr.getError()) { + std::string Error = EC.message(); + errs() << "Error loading index file '" << CodeGenOpts.ThinLTOIndexFile + << "': " << Error << "\n"; + return; + } + FunctionIndex = std::move(IndexOrErr.get()); + assert(FunctionIndex && "Expected non-empty function index"); + } + + CreatePasses(FunctionIndex.get()); switch (Action) { case Backend_EmitNothing: @@ -695,10 +714,8 @@ void clang::EmitBackendOutput(DiagnosticsEngine &Diags, const clang::TargetOptions &TOpts, const LangOptions &LOpts, StringRef TDesc, Module *M, BackendAction Action, - raw_pwrite_stream *OS, - std::unique_ptr<FunctionInfoIndex> Index) { - EmitAssemblyHelper AsmHelper(Diags, CGOpts, TOpts, LOpts, M, - std::move(Index)); + raw_pwrite_stream *OS) { + EmitAssemblyHelper AsmHelper(Diags, CGOpts, TOpts, LOpts, M); AsmHelper.EmitAssembly(Action, OS); diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 78e3978e0ff9..5df85194878d 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -2025,6 +2025,11 @@ llvm::DIType *CGDebugInfo::CreateType(const AtomicType *Ty, llvm::DIFile *U) { return getOrCreateType(Ty->getValueType(), U); } +llvm::DIType* CGDebugInfo::CreateType(const PipeType *Ty, + llvm::DIFile *U) { + return getOrCreateType(Ty->getElementType(), U); +} + llvm::DIType *CGDebugInfo::CreateEnumType(const EnumType *Ty) { const EnumDecl *ED = Ty->getDecl(); @@ -2284,6 +2289,9 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { case Type::Atomic: return CreateType(cast<AtomicType>(Ty), Unit); + case Type::Pipe: + return CreateType(cast<PipeType>(Ty), Unit); + case Type::TemplateSpecialization: return CreateType(cast<TemplateSpecializationType>(Ty), Unit); diff --git a/lib/CodeGen/CGDebugInfo.h b/lib/CodeGen/CGDebugInfo.h index 57d5c808f297..a68dd33fa5fe 100644 --- a/lib/CodeGen/CGDebugInfo.h +++ b/lib/CodeGen/CGDebugInfo.h @@ -168,6 +168,7 @@ class CGDebugInfo { llvm::DIType *CreateType(const RValueReferenceType *Ty, llvm::DIFile *Unit); llvm::DIType *CreateType(const MemberPointerType *Ty, llvm::DIFile *F); llvm::DIType *CreateType(const AtomicType *Ty, llvm::DIFile *F); + llvm::DIType *CreateType(const PipeType *Ty, llvm::DIFile *F); /// Get enumeration type. llvm::DIType *CreateEnumType(const EnumType *Ty); llvm::DIType *CreateTypeDefinition(const EnumType *Ty); diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index dabd2b1528bb..507ce3d7d0ce 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -3365,6 +3365,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { case CK_PointerToBoolean: case CK_VectorSplat: case CK_IntegralCast: + case CK_BooleanToSignedIntegral: case CK_IntegralToBoolean: case CK_IntegralToFloating: case CK_FloatingToIntegral: diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index 20838db044c9..a4547a9982be 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -721,6 +721,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CK_ToVoid: case CK_VectorSplat: case CK_IntegralCast: + case CK_BooleanToSignedIntegral: case CK_IntegralToBoolean: case CK_IntegralToFloating: case CK_FloatingToIntegral: diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp index ccdb53287e9f..22910d931ded 100644 --- a/lib/CodeGen/CGExprComplex.cpp +++ b/lib/CodeGen/CGExprComplex.cpp @@ -462,6 +462,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op, case CK_ToVoid: case CK_VectorSplat: case CK_IntegralCast: + case CK_BooleanToSignedIntegral: case CK_IntegralToBoolean: case CK_IntegralToFloating: case CK_FloatingToIntegral: diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index 3839ab718fa3..ee049f1810a2 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -735,6 +735,7 @@ public: case CK_PointerToBoolean: case CK_NullToPointer: case CK_IntegralCast: + case CK_BooleanToSignedIntegral: case CK_IntegralToPointer: case CK_IntegralToBoolean: case CK_IntegralToFloating: diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 725d96f29877..268e7967b808 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -811,14 +811,15 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, // A scalar can be splatted to an extended vector of the same element type if (DstType->isExtVectorType() && !SrcType->isVectorType()) { - // Cast the scalar to element type - QualType EltTy = DstType->getAs<ExtVectorType>()->getElementType(); - llvm::Value *Elt = EmitScalarConversion( - Src, SrcType, EltTy, Loc, CGF.getContext().getLangOpts().OpenCL); + // Sema should add casts to make sure that the source expression's type is + // the same as the vector's element type (sans qualifiers) + assert(DstType->castAs<ExtVectorType>()->getElementType().getTypePtr() == + SrcType.getTypePtr() && + "Splatted expr doesn't match with vector element type?"); // Splat the element across to all elements unsigned NumElements = cast<llvm::VectorType>(DstTy)->getNumElements(); - return Builder.CreateVectorSplat(NumElements, Elt, "splat"); + return Builder.CreateVectorSplat(NumElements, Src, "splat"); } // Allow bitcast from vector to integer/fp of the same size. @@ -1541,15 +1542,7 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { } case CK_VectorSplat: { llvm::Type *DstTy = ConvertType(DestTy); - // Need an IgnoreImpCasts here as by default a boolean will be promoted to - // an int, which will not perform the sign extension, so if we know we are - // going to cast to a vector we have to strip the implicit cast off. - Value *Elt = Visit(const_cast<Expr*>(E->IgnoreImpCasts())); - Elt = EmitScalarConversion(Elt, E->IgnoreImpCasts()->getType(), - DestTy->getAs<VectorType>()->getElementType(), - CE->getExprLoc(), - CGF.getContext().getLangOpts().OpenCL); - + Value *Elt = Visit(const_cast<Expr*>(E)); // Splat the element across to all elements unsigned NumElements = cast<llvm::VectorType>(DstTy)->getNumElements(); return Builder.CreateVectorSplat(NumElements, Elt, "splat"); @@ -1561,6 +1554,10 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { case CK_FloatingCast: return EmitScalarConversion(Visit(E), E->getType(), DestTy, CE->getExprLoc()); + case CK_BooleanToSignedIntegral: + return EmitScalarConversion(Visit(E), E->getType(), DestTy, + CE->getExprLoc(), + /*TreatBooleanAsSigned=*/true); case CK_IntegralToBoolean: return EmitIntToBoolConversion(Visit(E)); case CK_PointerToBoolean: diff --git a/lib/CodeGen/CGOpenCLRuntime.cpp b/lib/CodeGen/CGOpenCLRuntime.cpp index 8af39ceecdfe..686678962d3e 100644 --- a/lib/CodeGen/CGOpenCLRuntime.cpp +++ b/lib/CodeGen/CGOpenCLRuntime.cpp @@ -99,3 +99,14 @@ llvm::Type *CGOpenCLRuntime::convertOpenCLSpecificType(const Type *T) { llvm::StructType::create(Ctx, "opencl.reserve_id_t"), 0); } } + +llvm::Type *CGOpenCLRuntime::getPipeType() { + if (!PipeTy){ + uint32_t PipeAddrSpc = + CGM.getContext().getTargetAddressSpace(LangAS::opencl_global); + PipeTy = llvm::PointerType::get(llvm::StructType::create( + CGM.getLLVMContext(), "opencl.pipe_t"), PipeAddrSpc); + } + + return PipeTy; +} diff --git a/lib/CodeGen/CGOpenCLRuntime.h b/lib/CodeGen/CGOpenCLRuntime.h index 0c50b92914b8..f1a7a3106443 100644 --- a/lib/CodeGen/CGOpenCLRuntime.h +++ b/lib/CodeGen/CGOpenCLRuntime.h @@ -32,9 +32,10 @@ class CodeGenModule; class CGOpenCLRuntime { protected: CodeGenModule &CGM; + llvm::Type *PipeTy; public: - CGOpenCLRuntime(CodeGenModule &CGM) : CGM(CGM) {} + CGOpenCLRuntime(CodeGenModule &CGM) : CGM(CGM), PipeTy(nullptr) {} virtual ~CGOpenCLRuntime(); /// Emit the IR required for a work-group-local variable declaration, and add @@ -44,6 +45,8 @@ public: const VarDecl &D); virtual llvm::Type *convertOpenCLSpecificType(const Type *T); + + virtual llvm::Type *getPipeType(); }; } diff --git a/lib/CodeGen/CGOpenMPRuntime.cpp b/lib/CodeGen/CGOpenMPRuntime.cpp index 6d4fc9f64b47..3b97ba2469ae 100644 --- a/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/lib/CodeGen/CGOpenMPRuntime.cpp @@ -84,7 +84,7 @@ public: protected: CGOpenMPRegionKind RegionKind; - const RegionCodeGenTy &CodeGen; + RegionCodeGenTy CodeGen; OpenMPDirectiveKind Kind; bool HasCancel; }; diff --git a/lib/CodeGen/CGOpenMPRuntime.h b/lib/CodeGen/CGOpenMPRuntime.h index 6b04fbeb09b5..b3256375d9b6 100644 --- a/lib/CodeGen/CGOpenMPRuntime.h +++ b/lib/CodeGen/CGOpenMPRuntime.h @@ -335,7 +335,7 @@ private: public: /// \brief Kind of a given entry. Currently, only target regions are /// supported. - enum OffloadingEntryInfoKinds { + enum OffloadingEntryInfoKinds : unsigned { // Entry is a target region. OFFLOAD_ENTRY_INFO_TARGET_REGION = 0, // Invalid entry info. @@ -955,7 +955,7 @@ public: /// \brief Emit the target regions enclosed in \a GD function definition or /// the function itself in case it is a valid device function. Returns true if /// \a GD was dealt with successfully. - /// \param FD Function to scan. + /// \param GD Function to scan. virtual bool emitTargetFunctions(GlobalDecl GD); /// \brief Emit the global variable if it is a valid device global variable. diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index abef5432518e..0a670ab19aa5 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -26,12 +26,10 @@ #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" -#include "llvm/IR/FunctionInfo.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Linker/Linker.h" -#include "llvm/Object/FunctionIndexObjectFile.h" #include "llvm/Pass.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" @@ -781,43 +779,11 @@ void CodeGenAction::ExecuteAction() { TheModule->setTargetTriple(TargetOpts.Triple); } - auto DiagHandler = [&](const DiagnosticInfo &DI) { - TheModule->getContext().diagnose(DI); - }; - - // If we are performing ThinLTO importing compilation (indicated by - // a non-empty index file option), then we need promote to global scope - // and rename any local values that are potentially exported to other - // modules. Do this early so that the rest of the compilation sees the - // promoted symbols. - std::unique_ptr<FunctionInfoIndex> Index; - if (!CI.getCodeGenOpts().ThinLTOIndexFile.empty()) { - ErrorOr<std::unique_ptr<FunctionInfoIndex>> IndexOrErr = - llvm::getFunctionIndexForFile(CI.getCodeGenOpts().ThinLTOIndexFile, - DiagHandler); - if (std::error_code EC = IndexOrErr.getError()) { - std::string Error = EC.message(); - errs() << "Error loading index file '" - << CI.getCodeGenOpts().ThinLTOIndexFile << "': " << Error - << "\n"; - return; - } - Index = std::move(IndexOrErr.get()); - assert(Index); - // Currently this requires creating a new Module object. - std::unique_ptr<llvm::Module> RenamedModule = - renameModuleForThinLTO(std::move(TheModule), Index.get()); - if (!RenamedModule) - return; - - TheModule = std::move(RenamedModule); - } - LLVMContext &Ctx = TheModule->getContext(); Ctx.setInlineAsmDiagnosticHandler(BitcodeInlineAsmDiagHandler); EmitBackendOutput(CI.getDiagnostics(), CI.getCodeGenOpts(), TargetOpts, CI.getLangOpts(), CI.getTarget().getDataLayoutString(), - TheModule.get(), BA, OS, std::move(Index)); + TheModule.get(), BA, OS); return; } diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 048a04328fc2..e38ff0a39da3 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -79,7 +79,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) if (CGM.getCodeGenOpts().ReciprocalMath) { FMF.setAllowReciprocal(); } - Builder.SetFastMathFlags(FMF); + Builder.setFastMathFlags(FMF); } CodeGenFunction::~CodeGenFunction() { @@ -195,6 +195,7 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) { case Type::FunctionNoProto: case Type::Enum: case Type::ObjCObjectPointer: + case Type::Pipe: return TEK_Scalar; // Complexes. @@ -511,7 +512,8 @@ static void GenOpenCLArgMetadata(const FunctionDecl *FD, llvm::Function *Fn, typeQuals += typeQuals.empty() ? "volatile" : " volatile"; } else { uint32_t AddrSpc = 0; - if (ty->isImageType()) + bool isPipe = ty->isPipeType(); + if (ty->isImageType() || isPipe) AddrSpc = CGM.getContext().getTargetAddressSpace(LangAS::opencl_global); @@ -519,7 +521,11 @@ static void GenOpenCLArgMetadata(const FunctionDecl *FD, llvm::Function *Fn, llvm::ConstantAsMetadata::get(Builder.getInt32(AddrSpc))); // Get argument type name. - std::string typeName = ty.getUnqualifiedType().getAsString(Policy); + std::string typeName; + if (isPipe) + typeName = cast<PipeType>(ty)->getElementType().getAsString(Policy); + else + typeName = ty.getUnqualifiedType().getAsString(Policy); // Turn "unsigned type" to "utype" std::string::size_type pos = typeName.find("unsigned"); @@ -528,7 +534,12 @@ static void GenOpenCLArgMetadata(const FunctionDecl *FD, llvm::Function *Fn, argTypeNames.push_back(llvm::MDString::get(Context, typeName)); - std::string baseTypeName = + std::string baseTypeName; + if (isPipe) + baseTypeName = + cast<PipeType>(ty)->getElementType().getCanonicalType().getAsString(Policy); + else + baseTypeName = ty.getUnqualifiedType().getCanonicalType().getAsString(Policy); // Turn "unsigned type" to "utype" @@ -543,12 +554,16 @@ static void GenOpenCLArgMetadata(const FunctionDecl *FD, llvm::Function *Fn, typeQuals = "const"; if (ty.isVolatileQualified()) typeQuals += typeQuals.empty() ? "volatile" : " volatile"; + if (isPipe) + typeQuals = "pipe"; } argTypeQuals.push_back(llvm::MDString::get(Context, typeQuals)); - // Get image access qualifier: - if (ty->isImageType()) { + // Get image and pipe access qualifier: + // FIXME: now image and pipe share the same access qualifier maybe we can + // refine it to OpenCL access qualifier and also handle write_read + if (ty->isImageType()|| ty->isPipeType()) { const OpenCLImageAccessAttr *A = parm->getAttr<OpenCLImageAccessAttr>(); if (A && A->isWriteOnly()) accessQuals.push_back(llvm::MDString::get(Context, "write_only")); @@ -1727,6 +1742,10 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) { case Type::Atomic: type = cast<AtomicType>(ty)->getValueType(); break; + + case Type::Pipe: + type = cast<PipeType>(ty)->getElementType(); + break; } } while (type->isVariablyModifiedType()); } diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 536c55ae4e18..97b166278f81 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -615,7 +615,20 @@ void CodeGenModule::setTLSMode(llvm::GlobalValue *GV, const VarDecl &D) const { } StringRef CodeGenModule::getMangledName(GlobalDecl GD) { - StringRef &FoundStr = MangledDeclNames[GD.getCanonicalDecl()]; + GlobalDecl CanonicalGD = GD.getCanonicalDecl(); + + // Some ABIs don't have constructor variants. Make sure that base and + // complete constructors get mangled the same. + if (const auto *CD = dyn_cast<CXXConstructorDecl>(CanonicalGD.getDecl())) { + if (!getTarget().getCXXABI().hasConstructorVariants()) { + CXXCtorType OrigCtorType = GD.getCtorType(); + assert(OrigCtorType == Ctor_Base || OrigCtorType == Ctor_Complete); + if (OrigCtorType == Ctor_Base) + CanonicalGD = GlobalDecl(CD, Ctor_Complete); + } + } + + StringRef &FoundStr = MangledDeclNames[CanonicalGD]; if (!FoundStr.empty()) return FoundStr; diff --git a/lib/CodeGen/CodeGenPGO.cpp b/lib/CodeGen/CodeGenPGO.cpp index 5ae861eab0d3..2c0d93b394ac 100644 --- a/lib/CodeGen/CodeGenPGO.cpp +++ b/lib/CodeGen/CodeGenPGO.cpp @@ -699,7 +699,7 @@ CodeGenPGO::emitEmptyCounterMapping(const Decl *D, StringRef Name, setFuncName(Name, Linkage); CGM.getCoverageMapping()->addFunctionMappingRecord( - FuncNameVar, FuncName, FunctionHash, CoverageMapping); + FuncNameVar, FuncName, FunctionHash, CoverageMapping, false); } void CodeGenPGO::computeRegionCounts(const Decl *D) { diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp index fcda05320551..09d9bf17b3bf 100644 --- a/lib/CodeGen/CodeGenTypes.cpp +++ b/lib/CodeGen/CodeGenTypes.cpp @@ -628,6 +628,10 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { } break; } + case Type::Pipe: { + ResultType = CGM.getOpenCLRuntime().getPipeType(); + break; + } } assert(ResultType && "Didn't convert a type?"); diff --git a/lib/CodeGen/CoverageMappingGen.cpp b/lib/CodeGen/CoverageMappingGen.cpp index 1d4d7099e194..03e22cd398aa 100644 --- a/lib/CodeGen/CoverageMappingGen.cpp +++ b/lib/CodeGen/CoverageMappingGen.cpp @@ -910,11 +910,11 @@ static void dump(llvm::raw_ostream &OS, StringRef FunctionName, } void CoverageMappingModuleGen::addFunctionMappingRecord( - llvm::GlobalVariable *NamePtr, StringRef NameValue, - uint64_t FuncHash, const std::string &CoverageMapping) { + llvm::GlobalVariable *NamePtr, StringRef NameValue, uint64_t FuncHash, + const std::string &CoverageMapping, bool isUsed) { llvm::LLVMContext &Ctx = CGM.getLLVMContext(); if (!FunctionRecordTy) { - #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType, +#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType, llvm::Type *FunctionRecordTypes[] = { #include "llvm/ProfileData/InstrProfData.inc" }; @@ -929,6 +929,9 @@ void CoverageMappingModuleGen::addFunctionMappingRecord( }; FunctionRecords.push_back(llvm::ConstantStruct::get( FunctionRecordTy, makeArrayRef(FunctionRecordVals))); + if (!isUsed) + FunctionNames.push_back( + llvm::ConstantExpr::getBitCast(NamePtr, llvm::Type::getInt8PtrTy(Ctx))); CoverageMappings += CoverageMapping; if (CGM.getCodeGenOpts().DumpCoverageMapping) { @@ -1023,6 +1026,17 @@ void CoverageMappingModuleGen::emit() { // Make sure the data doesn't get deleted. CGM.addUsedGlobal(CovData); + // Create the deferred function records array + if (!FunctionNames.empty()) { + auto NamesArrTy = llvm::ArrayType::get(llvm::Type::getInt8PtrTy(Ctx), + FunctionNames.size()); + auto NamesArrVal = llvm::ConstantArray::get(NamesArrTy, FunctionNames); + // This variable will *NOT* be emitted to the object file. It is used + // to pass the list of names referenced to codegen. + new llvm::GlobalVariable(CGM.getModule(), NamesArrTy, true, + llvm::GlobalValue::InternalLinkage, NamesArrVal, + llvm::getCoverageNamesVarName()); + } } unsigned CoverageMappingModuleGen::getFileID(const FileEntry *File) { diff --git a/lib/CodeGen/CoverageMappingGen.h b/lib/CodeGen/CoverageMappingGen.h index 0d1bf6d975c9..9ae2bcffe4ca 100644 --- a/lib/CodeGen/CoverageMappingGen.h +++ b/lib/CodeGen/CoverageMappingGen.h @@ -54,6 +54,7 @@ class CoverageMappingModuleGen { CoverageSourceInfo &SourceInfo; llvm::SmallDenseMap<const FileEntry *, unsigned, 8> FileEntries; std::vector<llvm::Constant *> FunctionRecords; + std::vector<llvm::Constant *> FunctionNames; llvm::StructType *FunctionRecordTy; std::string CoverageMappings; @@ -70,7 +71,8 @@ public: void addFunctionMappingRecord(llvm::GlobalVariable *FunctionName, StringRef FunctionNameValue, uint64_t FunctionHash, - const std::string &CoverageMapping); + const std::string &CoverageMapping, + bool isUsed = true); /// \brief Emit the coverage mapping data for a translation unit. void emit(); diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 0c4008f8ee78..e02c8dc3a86a 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -2715,6 +2715,9 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty) { case Type::Auto: llvm_unreachable("Undeduced auto type shouldn't get here"); + case Type::Pipe: + llvm_unreachable("Pipe types shouldn't get here"); + case Type::Builtin: // GCC treats vector and complex types as fundamental types. case Type::Vector: @@ -2939,6 +2942,9 @@ llvm::Constant *ItaniumRTTIBuilder::BuildTypeInfo(QualType Ty, bool Force) { case Type::Auto: llvm_unreachable("Undeduced auto type shouldn't get here"); + case Type::Pipe: + llvm_unreachable("Pipe type shouldn't get here"); + case Type::ConstantArray: case Type::IncompleteArray: case Type::VariableArray: diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index b397eb352a60..f385e53fa01f 100644 --- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -59,8 +59,10 @@ class PCHContainerGenerator : public ASTConsumer { struct DebugTypeVisitor : public RecursiveASTVisitor<DebugTypeVisitor> { clang::CodeGen::CGDebugInfo &DI; ASTContext &Ctx; - DebugTypeVisitor(clang::CodeGen::CGDebugInfo &DI, ASTContext &Ctx) - : DI(DI), Ctx(Ctx) {} + bool SkipTagDecls; + DebugTypeVisitor(clang::CodeGen::CGDebugInfo &DI, ASTContext &Ctx, + bool SkipTagDecls) + : DI(DI), Ctx(Ctx), SkipTagDecls(SkipTagDecls) {} /// Determine whether this type can be represented in DWARF. static bool CanRepresent(const Type *Ty) { @@ -75,6 +77,12 @@ class PCHContainerGenerator : public ASTConsumer { } bool VisitTypeDecl(TypeDecl *D) { + // TagDecls may be deferred until after all decls have been merged and we + // know the complete type. Pure forward declarations will be skipped, but + // they don't need to be emitted into the module anyway. + if (SkipTagDecls && isa<TagDecl>(D)) + return true; + QualType QualTy = Ctx.getTypeDeclType(D); if (!QualTy.isNull() && CanRepresent(QualTy.getTypePtr())) DI.getOrCreateStandaloneType(QualTy, D->getLocation()); @@ -165,7 +173,7 @@ public: // Collect debug info for all decls in this group. for (auto *I : D) if (!I->isFromASTFile()) { - DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx); + DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx, true); DTV.TraverseDecl(I); } return true; @@ -179,6 +187,11 @@ public: if (Diags.hasErrorOccurred()) return; + if (D->isFromASTFile()) + return; + + DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx, false); + DTV.TraverseDecl(D); Builder->UpdateCompletedType(D); } diff --git a/lib/Driver/Action.cpp b/lib/Driver/Action.cpp index 49dccd224bff..e9490e96db8d 100644 --- a/lib/Driver/Action.cpp +++ b/lib/Driver/Action.cpp @@ -8,17 +8,14 @@ //===----------------------------------------------------------------------===// #include "clang/Driver/Action.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Regex.h" #include <cassert> using namespace clang::driver; using namespace llvm::opt; -Action::~Action() { - if (OwnsInputs) { - for (iterator it = begin(), ie = end(); it != ie; ++it) - delete *it; - } -} +Action::~Action() {} const char *Action::getClassName(ActionClass AC) { switch (AC) { @@ -51,33 +48,53 @@ InputAction::InputAction(const Arg &_Input, types::ID _Type) void BindArchAction::anchor() {} -BindArchAction::BindArchAction(std::unique_ptr<Action> Input, - const char *_ArchName) - : Action(BindArchClass, std::move(Input)), ArchName(_ArchName) {} +BindArchAction::BindArchAction(Action *Input, const char *_ArchName) + : Action(BindArchClass, Input), ArchName(_ArchName) {} + +// Converts CUDA GPU architecture, e.g. "sm_21", to its corresponding virtual +// compute arch, e.g. "compute_20". Returns null if the input arch is null or +// doesn't match an existing arch. +static const char* GpuArchToComputeName(const char *ArchName) { + if (!ArchName) + return nullptr; + return llvm::StringSwitch<const char *>(ArchName) + .Cases("sm_20", "sm_21", "compute_20") + .Case("sm_30", "compute_30") + .Case("sm_32", "compute_32") + .Case("sm_35", "compute_35") + .Case("sm_37", "compute_37") + .Case("sm_50", "compute_50") + .Case("sm_52", "compute_52") + .Case("sm_53", "compute_53") + .Default(nullptr); +} void CudaDeviceAction::anchor() {} -CudaDeviceAction::CudaDeviceAction(std::unique_ptr<Action> Input, - const char *ArchName, bool AtTopLevel) - : Action(CudaDeviceClass, std::move(Input)), GpuArchName(ArchName), - AtTopLevel(AtTopLevel) {} - -void CudaHostAction::anchor() {} +CudaDeviceAction::CudaDeviceAction(Action *Input, const char *ArchName, + bool AtTopLevel) + : Action(CudaDeviceClass, Input), GpuArchName(ArchName), + AtTopLevel(AtTopLevel) { + assert(IsValidGpuArchName(GpuArchName)); +} -CudaHostAction::CudaHostAction(std::unique_ptr<Action> Input, - const ActionList &DeviceActions) - : Action(CudaHostClass, std::move(Input)), DeviceActions(DeviceActions) {} +const char *CudaDeviceAction::getComputeArchName() const { + return GpuArchToComputeName(GpuArchName); +} -CudaHostAction::~CudaHostAction() { - for (auto &DA : DeviceActions) - delete DA; +bool CudaDeviceAction::IsValidGpuArchName(llvm::StringRef ArchName) { + return GpuArchToComputeName(ArchName.data()) != nullptr; } +void CudaHostAction::anchor() {} + +CudaHostAction::CudaHostAction(Action *Input, const ActionList &DeviceActions) + : Action(CudaHostClass, Input), DeviceActions(DeviceActions) {} + void JobAction::anchor() {} -JobAction::JobAction(ActionClass Kind, std::unique_ptr<Action> Input, - types::ID Type) - : Action(Kind, std::move(Input), Type) {} +JobAction::JobAction(ActionClass Kind, Action *Input, types::ID Type) + : Action(Kind, Input, Type) {} JobAction::JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type) : Action(Kind, Inputs, Type) { @@ -85,45 +102,38 @@ JobAction::JobAction(ActionClass Kind, const ActionList &Inputs, types::ID Type) void PreprocessJobAction::anchor() {} -PreprocessJobAction::PreprocessJobAction(std::unique_ptr<Action> Input, - types::ID OutputType) - : JobAction(PreprocessJobClass, std::move(Input), OutputType) {} +PreprocessJobAction::PreprocessJobAction(Action *Input, types::ID OutputType) + : JobAction(PreprocessJobClass, Input, OutputType) {} void PrecompileJobAction::anchor() {} -PrecompileJobAction::PrecompileJobAction(std::unique_ptr<Action> Input, - types::ID OutputType) - : JobAction(PrecompileJobClass, std::move(Input), OutputType) {} +PrecompileJobAction::PrecompileJobAction(Action *Input, types::ID OutputType) + : JobAction(PrecompileJobClass, Input, OutputType) {} void AnalyzeJobAction::anchor() {} -AnalyzeJobAction::AnalyzeJobAction(std::unique_ptr<Action> Input, - types::ID OutputType) - : JobAction(AnalyzeJobClass, std::move(Input), OutputType) {} +AnalyzeJobAction::AnalyzeJobAction(Action *Input, types::ID OutputType) + : JobAction(AnalyzeJobClass, Input, OutputType) {} void MigrateJobAction::anchor() {} -MigrateJobAction::MigrateJobAction(std::unique_ptr<Action> Input, - types::ID OutputType) - : JobAction(MigrateJobClass, std::move(Input), OutputType) {} +MigrateJobAction::MigrateJobAction(Action *Input, types::ID OutputType) + : JobAction(MigrateJobClass, Input, OutputType) {} void CompileJobAction::anchor() {} -CompileJobAction::CompileJobAction(std::unique_ptr<Action> Input, - types::ID OutputType) - : JobAction(CompileJobClass, std::move(Input), OutputType) {} +CompileJobAction::CompileJobAction(Action *Input, types::ID OutputType) + : JobAction(CompileJobClass, Input, OutputType) {} void BackendJobAction::anchor() {} -BackendJobAction::BackendJobAction(std::unique_ptr<Action> Input, - types::ID OutputType) - : JobAction(BackendJobClass, std::move(Input), OutputType) {} +BackendJobAction::BackendJobAction(Action *Input, types::ID OutputType) + : JobAction(BackendJobClass, Input, OutputType) {} void AssembleJobAction::anchor() {} -AssembleJobAction::AssembleJobAction(std::unique_ptr<Action> Input, - types::ID OutputType) - : JobAction(AssembleJobClass, std::move(Input), OutputType) {} +AssembleJobAction::AssembleJobAction(Action *Input, types::ID OutputType) + : JobAction(AssembleJobClass, Input, OutputType) {} void LinkJobAction::anchor() {} @@ -145,21 +155,20 @@ DsymutilJobAction::DsymutilJobAction(ActionList &Inputs, types::ID Type) void VerifyJobAction::anchor() {} -VerifyJobAction::VerifyJobAction(ActionClass Kind, - std::unique_ptr<Action> Input, types::ID Type) - : JobAction(Kind, std::move(Input), Type) { +VerifyJobAction::VerifyJobAction(ActionClass Kind, Action *Input, + types::ID Type) + : JobAction(Kind, Input, Type) { assert((Kind == VerifyDebugInfoJobClass || Kind == VerifyPCHJobClass) && "ActionClass is not a valid VerifyJobAction"); } void VerifyDebugInfoJobAction::anchor() {} -VerifyDebugInfoJobAction::VerifyDebugInfoJobAction( - std::unique_ptr<Action> Input, types::ID Type) - : VerifyJobAction(VerifyDebugInfoJobClass, std::move(Input), Type) {} +VerifyDebugInfoJobAction::VerifyDebugInfoJobAction(Action *Input, + types::ID Type) + : VerifyJobAction(VerifyDebugInfoJobClass, Input, Type) {} void VerifyPCHJobAction::anchor() {} -VerifyPCHJobAction::VerifyPCHJobAction(std::unique_ptr<Action> Input, - types::ID Type) - : VerifyJobAction(VerifyPCHJobClass, std::move(Input), Type) {} +VerifyPCHJobAction::VerifyPCHJobAction(Action *Input, types::ID Type) + : VerifyJobAction(VerifyPCHJobClass, Input, Type) {} diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index e4af2a6ced8a..1c2eecd3ccc5 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -40,11 +40,6 @@ Compilation::~Compilation() { if (it->second != TranslatedArgs) delete it->second; - // Free the actions, if built. - for (ActionList::iterator it = Actions.begin(), ie = Actions.end(); - it != ie; ++it) - delete *it; - // Free redirections of stdout/stderr. if (Redirects) { delete Redirects[1]; @@ -208,7 +203,8 @@ void Compilation::initCompilationForDiagnostics() { ForDiagnostics = true; // Free actions and jobs. - DeleteContainerPointers(Actions); + Actions.clear(); + AllActions.clear(); Jobs.clear(); // Clear temporary/results file lists. diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 85bbcb4113ab..1e0a48d52928 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1049,19 +1049,15 @@ void Driver::BuildUniversalActions(Compilation &C, const ToolChain &TC, << types::getTypeName(Act->getType()); ActionList Inputs; - for (unsigned i = 0, e = Archs.size(); i != e; ++i) { - Inputs.push_back( - new BindArchAction(std::unique_ptr<Action>(Act), Archs[i])); - if (i != 0) - Inputs.back()->setOwnsInputs(false); - } + for (unsigned i = 0, e = Archs.size(); i != e; ++i) + Inputs.push_back(C.MakeAction<BindArchAction>(Act, Archs[i])); // Lipo if necessary, we do it this way because we need to set the arch flag // so that -Xarch_ gets overwritten. if (Inputs.size() == 1 || Act->getType() == types::TY_Nothing) Actions.append(Inputs.begin(), Inputs.end()); else - Actions.push_back(new LipoJobAction(Inputs, Act->getType())); + Actions.push_back(C.MakeAction<LipoJobAction>(Inputs, Act->getType())); // Handle debug info queries. Arg *A = Args.getLastArg(options::OPT_g_Group); @@ -1077,15 +1073,16 @@ void Driver::BuildUniversalActions(Compilation &C, const ToolChain &TC, ActionList Inputs; Inputs.push_back(Actions.back()); Actions.pop_back(); - Actions.push_back(new DsymutilJobAction(Inputs, types::TY_dSYM)); + Actions.push_back( + C.MakeAction<DsymutilJobAction>(Inputs, types::TY_dSYM)); } // Verify the debug info output. if (Args.hasArg(options::OPT_verify_debug_info)) { - std::unique_ptr<Action> VerifyInput(Actions.back()); + Action* LastAction = Actions.back(); Actions.pop_back(); - Actions.push_back(new VerifyDebugInfoJobAction(std::move(VerifyInput), - types::TY_Nothing)); + Actions.push_back(C.MakeAction<VerifyDebugInfoJobAction>( + LastAction, types::TY_Nothing)); } } } @@ -1283,26 +1280,29 @@ void Driver::BuildInputs(const ToolChain &TC, DerivedArgList &Args, // Actions and /p Current is released. Otherwise the function creates // and returns a new CudaHostAction which wraps /p Current and device // side actions. -static std::unique_ptr<Action> -buildCudaActions(Compilation &C, DerivedArgList &Args, const Arg *InputArg, - std::unique_ptr<Action> HostAction, ActionList &Actions) { +static Action *buildCudaActions(Compilation &C, DerivedArgList &Args, + const Arg *InputArg, Action *HostAction, + ActionList &Actions) { Arg *PartialCompilationArg = Args.getLastArg(options::OPT_cuda_host_only, options::OPT_cuda_device_only); // Host-only compilation case. if (PartialCompilationArg && PartialCompilationArg->getOption().matches(options::OPT_cuda_host_only)) - return std::unique_ptr<Action>( - new CudaHostAction(std::move(HostAction), {})); + return C.MakeAction<CudaHostAction>(HostAction, ActionList()); // Collect all cuda_gpu_arch parameters, removing duplicates. SmallVector<const char *, 4> GpuArchList; llvm::StringSet<> GpuArchNames; for (Arg *A : Args) { - if (A->getOption().matches(options::OPT_cuda_gpu_arch_EQ)) { - A->claim(); - if (GpuArchNames.insert(A->getValue()).second) - GpuArchList.push_back(A->getValue()); - } + if (!A->getOption().matches(options::OPT_cuda_gpu_arch_EQ)) + continue; + A->claim(); + + const auto& Arch = A->getValue(); + if (!CudaDeviceAction::IsValidGpuArchName(Arch)) + C.getDriver().Diag(clang::diag::err_drv_cuda_bad_gpu_arch) << Arch; + else if (GpuArchNames.insert(Arch).second) + GpuArchList.push_back(Arch); } // Default to sm_20 which is the lowest common denominator for supported GPUs. @@ -1325,13 +1325,10 @@ buildCudaActions(Compilation &C, DerivedArgList &Args, const Arg *InputArg, "Failed to create actions for all devices"); // Check whether any of device actions stopped before they could generate PTX. - bool PartialCompilation = false; - for (unsigned I = 0, E = GpuArchList.size(); I != E; ++I) { - if (CudaDeviceActions[I]->getKind() != Action::BackendJobClass) { - PartialCompilation = true; - break; - } - } + bool PartialCompilation = + llvm::any_of(CudaDeviceActions, [](const Action *a) { + return a->getKind() != Action::BackendJobClass; + }); // Figure out what to do with device actions -- pass them as inputs to the // host action or run each of them independently. @@ -1350,12 +1347,12 @@ buildCudaActions(Compilation &C, DerivedArgList &Args, const Arg *InputArg, } for (unsigned I = 0, E = GpuArchList.size(); I != E; ++I) - Actions.push_back(new CudaDeviceAction( - std::unique_ptr<Action>(CudaDeviceActions[I]), GpuArchList[I], - /* AtTopLevel */ true)); + Actions.push_back(C.MakeAction<CudaDeviceAction>(CudaDeviceActions[I], + GpuArchList[I], + /* AtTopLevel */ true)); // Kill host action in case of device-only compilation. if (DeviceOnlyCompilation) - HostAction.reset(nullptr); + return nullptr; return HostAction; } @@ -1363,13 +1360,12 @@ buildCudaActions(Compilation &C, DerivedArgList &Args, const Arg *InputArg, // with AtTopLevel=false and become inputs for the host action. ActionList DeviceActions; for (unsigned I = 0, E = GpuArchList.size(); I != E; ++I) - DeviceActions.push_back(new CudaDeviceAction( - std::unique_ptr<Action>(CudaDeviceActions[I]), GpuArchList[I], - /* AtTopLevel */ false)); + DeviceActions.push_back( + C.MakeAction<CudaDeviceAction>(CudaDeviceActions[I], GpuArchList[I], + /* AtTopLevel */ false)); // Return a new host action that incorporates original host action and all // device actions. - return std::unique_ptr<Action>( - new CudaHostAction(std::move(HostAction), DeviceActions)); + return C.MakeAction<CudaHostAction>(HostAction, DeviceActions); } void Driver::BuildActions(Compilation &C, const ToolChain &TC, @@ -1470,15 +1466,14 @@ void Driver::BuildActions(Compilation &C, const ToolChain &TC, continue; } - phases::ID CudaInjectionPhase = FinalPhase; - for (const auto &Phase : PL) - if (Phase <= FinalPhase && Phase == phases::Compile) { - CudaInjectionPhase = Phase; - break; - } + phases::ID CudaInjectionPhase = + (phases::Compile < FinalPhase && + llvm::find(PL, phases::Compile) != PL.end()) + ? phases::Compile + : FinalPhase; // Build the pipeline for this file. - std::unique_ptr<Action> Current(new InputAction(*InputArg, InputType)); + Action *Current = C.MakeAction<InputAction>(*InputArg, InputType); for (SmallVectorImpl<phases::ID>::iterator i = PL.begin(), e = PL.end(); i != e; ++i) { phases::ID Phase = *i; @@ -1490,7 +1485,8 @@ void Driver::BuildActions(Compilation &C, const ToolChain &TC, // Queue linker inputs. if (Phase == phases::Link) { assert((i + 1) == e && "linking must be final compilation step."); - LinkerInputs.push_back(Current.release()); + LinkerInputs.push_back(Current); + Current = nullptr; break; } @@ -1501,11 +1497,10 @@ void Driver::BuildActions(Compilation &C, const ToolChain &TC, continue; // Otherwise construct the appropriate action. - Current = ConstructPhaseAction(TC, Args, Phase, std::move(Current)); + Current = ConstructPhaseAction(C, TC, Args, Phase, Current); if (InputType == types::TY_CUDA && Phase == CudaInjectionPhase) { - Current = - buildCudaActions(C, Args, InputArg, std::move(Current), Actions); + Current = buildCudaActions(C, Args, InputArg, Current, Actions); if (!Current) break; } @@ -1516,12 +1511,13 @@ void Driver::BuildActions(Compilation &C, const ToolChain &TC, // If we ended with something, add to the output list. if (Current) - Actions.push_back(Current.release()); + Actions.push_back(Current); } // Add a link action if necessary. if (!LinkerInputs.empty()) - Actions.push_back(new LinkJobAction(LinkerInputs, types::TY_Image)); + Actions.push_back( + C.MakeAction<LinkJobAction>(LinkerInputs, types::TY_Image)); // If we are linking, claim any options which are obviously only used for // compilation. @@ -1538,10 +1534,9 @@ void Driver::BuildActions(Compilation &C, const ToolChain &TC, Args.ClaimAllArgs(options::OPT_cuda_host_only); } -std::unique_ptr<Action> -Driver::ConstructPhaseAction(const ToolChain &TC, const ArgList &Args, - phases::ID Phase, - std::unique_ptr<Action> Input) const { +Action *Driver::ConstructPhaseAction(Compilation &C, const ToolChain &TC, + const ArgList &Args, phases::ID Phase, + Action *Input) const { llvm::PrettyStackTraceString CrashInfo("Constructing phase actions"); // Build the appropriate action. switch (Phase) { @@ -1561,7 +1556,7 @@ Driver::ConstructPhaseAction(const ToolChain &TC, const ArgList &Args, assert(OutputTy != types::TY_INVALID && "Cannot preprocess this input type!"); } - return llvm::make_unique<PreprocessJobAction>(std::move(Input), OutputTy); + return C.MakeAction<PreprocessJobAction>(Input, OutputTy); } case phases::Precompile: { types::ID OutputTy = types::TY_PCH; @@ -1569,53 +1564,43 @@ Driver::ConstructPhaseAction(const ToolChain &TC, const ArgList &Args, // Syntax checks should not emit a PCH file OutputTy = types::TY_Nothing; } - return llvm::make_unique<PrecompileJobAction>(std::move(Input), OutputTy); + return C.MakeAction<PrecompileJobAction>(Input, OutputTy); } case phases::Compile: { if (Args.hasArg(options::OPT_fsyntax_only)) - return llvm::make_unique<CompileJobAction>(std::move(Input), - types::TY_Nothing); + return C.MakeAction<CompileJobAction>(Input, types::TY_Nothing); if (Args.hasArg(options::OPT_rewrite_objc)) - return llvm::make_unique<CompileJobAction>(std::move(Input), - types::TY_RewrittenObjC); + return C.MakeAction<CompileJobAction>(Input, types::TY_RewrittenObjC); if (Args.hasArg(options::OPT_rewrite_legacy_objc)) - return llvm::make_unique<CompileJobAction>(std::move(Input), - types::TY_RewrittenLegacyObjC); + return C.MakeAction<CompileJobAction>(Input, + types::TY_RewrittenLegacyObjC); if (Args.hasArg(options::OPT__analyze, options::OPT__analyze_auto)) - return llvm::make_unique<AnalyzeJobAction>(std::move(Input), - types::TY_Plist); + return C.MakeAction<AnalyzeJobAction>(Input, types::TY_Plist); if (Args.hasArg(options::OPT__migrate)) - return llvm::make_unique<MigrateJobAction>(std::move(Input), - types::TY_Remap); + return C.MakeAction<MigrateJobAction>(Input, types::TY_Remap); if (Args.hasArg(options::OPT_emit_ast)) - return llvm::make_unique<CompileJobAction>(std::move(Input), - types::TY_AST); + return C.MakeAction<CompileJobAction>(Input, types::TY_AST); if (Args.hasArg(options::OPT_module_file_info)) - return llvm::make_unique<CompileJobAction>(std::move(Input), - types::TY_ModuleFile); + return C.MakeAction<CompileJobAction>(Input, types::TY_ModuleFile); if (Args.hasArg(options::OPT_verify_pch)) - return llvm::make_unique<VerifyPCHJobAction>(std::move(Input), - types::TY_Nothing); - return llvm::make_unique<CompileJobAction>(std::move(Input), - types::TY_LLVM_BC); + return C.MakeAction<VerifyPCHJobAction>(Input, types::TY_Nothing); + return C.MakeAction<CompileJobAction>(Input, types::TY_LLVM_BC); } case phases::Backend: { if (isUsingLTO()) { types::ID Output = Args.hasArg(options::OPT_S) ? types::TY_LTO_IR : types::TY_LTO_BC; - return llvm::make_unique<BackendJobAction>(std::move(Input), Output); + return C.MakeAction<BackendJobAction>(Input, Output); } if (Args.hasArg(options::OPT_emit_llvm)) { types::ID Output = Args.hasArg(options::OPT_S) ? types::TY_LLVM_IR : types::TY_LLVM_BC; - return llvm::make_unique<BackendJobAction>(std::move(Input), Output); + return C.MakeAction<BackendJobAction>(Input, Output); } - return llvm::make_unique<BackendJobAction>(std::move(Input), - types::TY_PP_Asm); + return C.MakeAction<BackendJobAction>(Input, types::TY_PP_Asm); } case phases::Assemble: - return llvm::make_unique<AssembleJobAction>(std::move(Input), - types::TY_Object); + return C.MakeAction<AssembleJobAction>(Input, types::TY_Object); } llvm_unreachable("invalid phase in ConstructPhaseAction"); @@ -1662,12 +1647,11 @@ void Driver::BuildJobs(Compilation &C) const { LinkingOutput = getDefaultImageName(); } - InputInfo II; BuildJobsForAction(C, A, &C.getDefaultToolChain(), /*BoundArch*/ nullptr, /*AtTopLevel*/ true, /*MultipleArchs*/ ArchNames.size() > 1, - /*LinkingOutput*/ LinkingOutput, II); + /*LinkingOutput*/ LinkingOutput); } // If the user passed -Qunused-arguments or there were errors, don't warn @@ -1795,21 +1779,19 @@ static const Tool *selectToolForJob(Compilation &C, bool SaveTemps, return ToolForJob; } -void Driver::BuildJobsForAction(Compilation &C, const Action *A, - const ToolChain *TC, const char *BoundArch, - bool AtTopLevel, bool MultipleArchs, - const char *LinkingOutput, - InputInfo &Result) const { +InputInfo Driver::BuildJobsForAction(Compilation &C, const Action *A, + const ToolChain *TC, const char *BoundArch, + bool AtTopLevel, bool MultipleArchs, + const char *LinkingOutput) const { llvm::PrettyStackTraceString CrashInfo("Building compilation jobs"); InputInfoList CudaDeviceInputInfos; if (const CudaHostAction *CHA = dyn_cast<CudaHostAction>(A)) { - InputInfo II; // Append outputs of device jobs to the input list. for (const Action *DA : CHA->getDeviceActions()) { - BuildJobsForAction(C, DA, TC, nullptr, AtTopLevel, - /*MultipleArchs*/ false, LinkingOutput, II); - CudaDeviceInputInfos.push_back(II); + CudaDeviceInputInfos.push_back( + BuildJobsForAction(C, DA, TC, nullptr, AtTopLevel, + /*MultipleArchs*/ false, LinkingOutput)); } // Override current action with a real host compile action and continue // processing it. @@ -1823,11 +1805,9 @@ void Driver::BuildJobsForAction(Compilation &C, const Action *A, Input.claim(); if (Input.getOption().matches(options::OPT_INPUT)) { const char *Name = Input.getValue(); - Result = InputInfo(Name, A->getType(), Name); - } else { - Result = InputInfo(&Input, A->getType(), ""); + return InputInfo(A, Name, /* BaseInput = */ Name); } - return; + return InputInfo(A, &Input, /* BaseInput = */ ""); } if (const BindArchAction *BAA = dyn_cast<BindArchAction>(A)) { @@ -1841,19 +1821,17 @@ void Driver::BuildJobsForAction(Compilation &C, const Action *A, else TC = &C.getDefaultToolChain(); - BuildJobsForAction(C, *BAA->begin(), TC, ArchName, AtTopLevel, - MultipleArchs, LinkingOutput, Result); - return; + return BuildJobsForAction(C, *BAA->begin(), TC, ArchName, AtTopLevel, + MultipleArchs, LinkingOutput); } if (const CudaDeviceAction *CDA = dyn_cast<CudaDeviceAction>(A)) { // Initial processing of CudaDeviceAction carries host params. // Call BuildJobsForAction() again, now with correct device parameters. assert(CDA->getGpuArchName() && "No GPU name in device action."); - BuildJobsForAction(C, *CDA->begin(), C.getCudaDeviceToolChain(), - CDA->getGpuArchName(), CDA->isAtTopLevel(), - /*MultipleArchs*/ true, LinkingOutput, Result); - return; + return BuildJobsForAction(C, *CDA->begin(), C.getCudaDeviceToolChain(), + CDA->getGpuArchName(), CDA->isAtTopLevel(), + /*MultipleArchs*/ true, LinkingOutput); } const ActionList *Inputs = &A->getInputs(); @@ -1863,16 +1841,15 @@ void Driver::BuildJobsForAction(Compilation &C, const Action *A, const Tool *T = selectToolForJob(C, isSaveTempsEnabled(), TC, JA, Inputs, CollapsedCHA); if (!T) - return; + return InputInfo(); // If we've collapsed action list that contained CudaHostAction we // need to build jobs for device-side inputs it may have held. if (CollapsedCHA) { - InputInfo II; for (const Action *DA : CollapsedCHA->getDeviceActions()) { - BuildJobsForAction(C, DA, TC, "", AtTopLevel, - /*MultipleArchs*/ false, LinkingOutput, II); - CudaDeviceInputInfos.push_back(II); + CudaDeviceInputInfos.push_back( + BuildJobsForAction(C, DA, TC, "", AtTopLevel, + /*MultipleArchs*/ false, LinkingOutput)); } } @@ -1882,14 +1859,11 @@ void Driver::BuildJobsForAction(Compilation &C, const Action *A, // Treat dsymutil and verify sub-jobs as being at the top-level too, they // shouldn't get temporary output names. // FIXME: Clean this up. - bool SubJobAtTopLevel = false; - if (AtTopLevel && (isa<DsymutilJobAction>(A) || isa<VerifyJobAction>(A))) - SubJobAtTopLevel = true; - - InputInfo II; - BuildJobsForAction(C, Input, TC, BoundArch, SubJobAtTopLevel, MultipleArchs, - LinkingOutput, II); - InputInfos.push_back(II); + bool SubJobAtTopLevel = + AtTopLevel && (isa<DsymutilJobAction>(A) || isa<VerifyJobAction>(A)); + InputInfos.push_back(BuildJobsForAction(C, Input, TC, BoundArch, + SubJobAtTopLevel, MultipleArchs, + LinkingOutput)); } // Always use the first input as the base input. @@ -1905,12 +1879,13 @@ void Driver::BuildJobsForAction(Compilation &C, const Action *A, InputInfos.append(CudaDeviceInputInfos.begin(), CudaDeviceInputInfos.end()); // Determine the place to write output to, if any. + InputInfo Result; if (JA->getType() == types::TY_Nothing) - Result = InputInfo(A->getType(), BaseInput); + Result = InputInfo(A, BaseInput); else - Result = InputInfo(GetNamedOutputPath(C, *JA, BaseInput, BoundArch, - AtTopLevel, MultipleArchs), - A->getType(), BaseInput); + Result = InputInfo(A, GetNamedOutputPath(C, *JA, BaseInput, BoundArch, + AtTopLevel, MultipleArchs), + BaseInput); if (CCCPrintBindings && !CCGenDiagnostics) { llvm::errs() << "# \"" << T->getToolChain().getTripleString() << '"' @@ -1925,6 +1900,7 @@ void Driver::BuildJobsForAction(Compilation &C, const Action *A, T->ConstructJob(C, *JA, Result, InputInfos, C.getArgsForToolChain(TC, BoundArch), LinkingOutput); } + return Result; } const char *Driver::getDefaultImageName() const { diff --git a/lib/Driver/InputInfo.h b/lib/Driver/InputInfo.h index b23ba575b65e..0c36e817c135 100644 --- a/lib/Driver/InputInfo.h +++ b/lib/Driver/InputInfo.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_LIB_DRIVER_INPUTINFO_H #define LLVM_CLANG_LIB_DRIVER_INPUTINFO_H +#include "clang/Driver/Action.h" #include "clang/Driver/Types.h" #include "llvm/Option/Arg.h" #include <cassert> @@ -38,21 +39,36 @@ class InputInfo { const llvm::opt::Arg *InputArg; } Data; Class Kind; + const Action* Act; types::ID Type; const char *BaseInput; + static types::ID GetActionType(const Action *A) { + return A != nullptr ? A->getType() : types::TY_Nothing; + } + public: - InputInfo() {} - InputInfo(types::ID _Type, const char *_BaseInput) - : Kind(Nothing), Type(_Type), BaseInput(_BaseInput) { + InputInfo() : InputInfo(nullptr, nullptr) {} + InputInfo(const Action *A, const char *_BaseInput) + : Kind(Nothing), Act(A), Type(GetActionType(A)), BaseInput(_BaseInput) {} + + InputInfo(types::ID _Type, const char *_Filename, const char *_BaseInput) + : Kind(Filename), Act(nullptr), Type(_Type), BaseInput(_BaseInput) { + Data.Filename = _Filename; } - InputInfo(const char *_Filename, types::ID _Type, const char *_BaseInput) - : Kind(Filename), Type(_Type), BaseInput(_BaseInput) { + InputInfo(const Action *A, const char *_Filename, const char *_BaseInput) + : Kind(Filename), Act(A), Type(GetActionType(A)), BaseInput(_BaseInput) { Data.Filename = _Filename; } - InputInfo(const llvm::opt::Arg *_InputArg, types::ID _Type, + + InputInfo(types::ID _Type, const llvm::opt::Arg *_InputArg, + const char *_BaseInput) + : Kind(InputArg), Act(nullptr), Type(_Type), BaseInput(_BaseInput) { + Data.InputArg = _InputArg; + } + InputInfo(const Action *A, const llvm::opt::Arg *_InputArg, const char *_BaseInput) - : Kind(InputArg), Type(_Type), BaseInput(_BaseInput) { + : Kind(InputArg), Act(A), Type(GetActionType(A)), BaseInput(_BaseInput) { Data.InputArg = _InputArg; } @@ -61,6 +77,9 @@ public: bool isInputArg() const { return Kind == InputArg; } types::ID getType() const { return Type; } const char *getBaseInput() const { return BaseInput; } + /// The action for which this InputInfo was created. May be null. + const Action *getAction() const { return Act; } + void setAction(const Action *A) { Act = A; } const char *getFilename() const { assert(isFilename() && "Invalid accessor."); diff --git a/lib/Driver/MSVCToolChain.cpp b/lib/Driver/MSVCToolChain.cpp index b7e576e53e8f..68747155b81c 100644 --- a/lib/Driver/MSVCToolChain.cpp +++ b/lib/Driver/MSVCToolChain.cpp @@ -634,6 +634,96 @@ SanitizerMask MSVCToolChain::getSupportedSanitizers() const { return Res; } +static void TranslateOptArg(Arg *A, llvm::opt::DerivedArgList &DAL, + bool SupportsForcingFramePointer, + const char *ExpandChar, const OptTable &Opts) { + assert(A->getOption().matches(options::OPT__SLASH_O)); + + StringRef OptStr = A->getValue(); + for (size_t I = 0, E = OptStr.size(); I != E; ++I) { + const char &OptChar = *(OptStr.data() + I); + switch (OptChar) { + default: + break; + case '1': + case '2': + case 'x': + case 'd': + if (&OptChar == ExpandChar) { + if (OptChar == 'd') { + DAL.AddFlagArg(A, Opts.getOption(options::OPT_O0)); + } else { + if (OptChar == '1') { + DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s"); + } else if (OptChar == '2' || OptChar == 'x') { + DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin)); + DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "2"); + } + if (SupportsForcingFramePointer) + DAL.AddFlagArg(A, + Opts.getOption(options::OPT_fomit_frame_pointer)); + if (OptChar == '1' || OptChar == '2') + DAL.AddFlagArg(A, + Opts.getOption(options::OPT_ffunction_sections)); + } + } + break; + case 'b': + if (I + 1 != E && isdigit(OptStr[I + 1])) + ++I; + break; + case 'g': + break; + case 'i': + if (I + 1 != E && OptStr[I + 1] == '-') { + ++I; + DAL.AddFlagArg(A, Opts.getOption(options::OPT_fno_builtin)); + } else { + DAL.AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin)); + } + break; + case 's': + DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "s"); + break; + case 't': + DAL.AddJoinedArg(A, Opts.getOption(options::OPT_O), "2"); + break; + case 'y': { + bool OmitFramePointer = true; + if (I + 1 != E && OptStr[I + 1] == '-') { + OmitFramePointer = false; + ++I; + } + if (SupportsForcingFramePointer) { + if (OmitFramePointer) + DAL.AddFlagArg(A, + Opts.getOption(options::OPT_fomit_frame_pointer)); + else + DAL.AddFlagArg( + A, Opts.getOption(options::OPT_fno_omit_frame_pointer)); + } + break; + } + } + } +} + +static void TranslateDArg(Arg *A, llvm::opt::DerivedArgList &DAL, + const OptTable &Opts) { + assert(A->getOption().matches(options::OPT_D)); + + StringRef Val = A->getValue(); + size_t Hash = Val.find('#'); + if (Hash == StringRef::npos || Hash > Val.find('=')) { + DAL.append(A); + return; + } + + std::string NewVal = Val; + NewVal[Hash] = '='; + DAL.AddJoinedArg(A, Opts.getOption(options::OPT_D), NewVal); +} + llvm::opt::DerivedArgList * MSVCToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args, const char *BoundArch) const { @@ -664,81 +754,18 @@ MSVCToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args, } } - // The -O flag actually takes an amalgam of other options. For example, - // '/Ogyb2' is equivalent to '/Og' '/Oy' '/Ob2'. for (Arg *A : Args) { - if (!A->getOption().matches(options::OPT__SLASH_O)) { + if (A->getOption().matches(options::OPT__SLASH_O)) { + // The -O flag actually takes an amalgam of other options. For example, + // '/Ogyb2' is equivalent to '/Og' '/Oy' '/Ob2'. + TranslateOptArg(A, *DAL, SupportsForcingFramePointer, ExpandChar, Opts); + } else if (A->getOption().matches(options::OPT_D)) { + // Translate -Dfoo#bar into -Dfoo=bar. + TranslateDArg(A, *DAL, Opts); + } else { DAL->append(A); - continue; - } - - StringRef OptStr = A->getValue(); - for (size_t I = 0, E = OptStr.size(); I != E; ++I) { - const char &OptChar = *(OptStr.data() + I); - switch (OptChar) { - default: - break; - case '1': - case '2': - case 'x': - case 'd': - if (&OptChar == ExpandChar) { - if (OptChar == 'd') { - DAL->AddFlagArg(A, Opts.getOption(options::OPT_O0)); - } else { - if (OptChar == '1') { - DAL->AddJoinedArg(A, Opts.getOption(options::OPT_O), "s"); - } else if (OptChar == '2' || OptChar == 'x') { - DAL->AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin)); - DAL->AddJoinedArg(A, Opts.getOption(options::OPT_O), "2"); - } - if (SupportsForcingFramePointer) - DAL->AddFlagArg(A, - Opts.getOption(options::OPT_fomit_frame_pointer)); - if (OptChar == '1' || OptChar == '2') - DAL->AddFlagArg(A, - Opts.getOption(options::OPT_ffunction_sections)); - } - } - break; - case 'b': - if (I + 1 != E && isdigit(OptStr[I + 1])) - ++I; - break; - case 'g': - break; - case 'i': - if (I + 1 != E && OptStr[I + 1] == '-') { - ++I; - DAL->AddFlagArg(A, Opts.getOption(options::OPT_fno_builtin)); - } else { - DAL->AddFlagArg(A, Opts.getOption(options::OPT_fbuiltin)); - } - break; - case 's': - DAL->AddJoinedArg(A, Opts.getOption(options::OPT_O), "s"); - break; - case 't': - DAL->AddJoinedArg(A, Opts.getOption(options::OPT_O), "2"); - break; - case 'y': { - bool OmitFramePointer = true; - if (I + 1 != E && OptStr[I + 1] == '-') { - OmitFramePointer = false; - ++I; - } - if (SupportsForcingFramePointer) { - if (OmitFramePointer) - DAL->AddFlagArg(A, - Opts.getOption(options::OPT_fomit_frame_pointer)); - else - DAL->AddFlagArg( - A, Opts.getOption(options::OPT_fno_omit_frame_pointer)); - } - break; - } - } } } + return DAL; } diff --git a/lib/Driver/MinGWToolChain.cpp b/lib/Driver/MinGWToolChain.cpp index c5287bb41575..938440b08f60 100644 --- a/lib/Driver/MinGWToolChain.cpp +++ b/lib/Driver/MinGWToolChain.cpp @@ -66,17 +66,23 @@ MinGW::MinGW(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) : ToolChain(D, Triple, Args) { getProgramPaths().push_back(getDriver().getInstalledDir()); - // On Windows if there is no sysroot we search for gcc on the PATH. - if (getDriver().SysRoot.size()) - Base = getDriver().SysRoot; +// In Windows there aren't any standard install locations, we search +// for gcc on the PATH. In Linux the base is always /usr. #ifdef LLVM_ON_WIN32 + if (getDriver().SysRoot.size()) + Base = getDriver().SysRoot; else if (llvm::ErrorOr<std::string> GPPName = llvm::sys::findProgramByName("gcc")) Base = llvm::sys::path::parent_path( llvm::sys::path::parent_path(GPPName.get())); -#endif - if (!Base.size()) + else Base = llvm::sys::path::parent_path(getDriver().getInstalledDir()); +#else + if (getDriver().SysRoot.size()) + Base = getDriver().SysRoot; + else + Base = "/usr"; +#endif Base += llvm::sys::path::get_separator(); findGccLibDir(); diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 7ece3216297f..beede2e05804 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -526,7 +526,7 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { // no environment variable defined, see if we can set the default based // on -isysroot. if (OSXTarget.empty() && iOSTarget.empty() && WatchOSTarget.empty() && - Args.hasArg(options::OPT_isysroot)) { + TvOSTarget.empty() && Args.hasArg(options::OPT_isysroot)) { if (const Arg *A = Args.getLastArg(options::OPT_isysroot)) { StringRef isysroot = A->getValue(); // Assume SDK has path: SOME_PATH/SDKs/PlatformXX.YY.sdk @@ -2716,13 +2716,8 @@ const StringRef HexagonToolChain::GetDefaultCPU() { const StringRef HexagonToolChain::GetTargetCPUVersion(const ArgList &Args) { Arg *CpuArg = nullptr; - - for (auto &A : Args) { - if (A->getOption().matches(options::OPT_mcpu_EQ)) { - CpuArg = A; - A->claim(); - } - } + if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ, options::OPT_march_EQ)) + CpuArg = A; StringRef CPU = CpuArg ? CpuArg->getValue() : GetDefaultCPU(); if (CPU.startswith("hexagon")) diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index f4b6b1529b30..b6fd42696ed4 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -730,9 +730,6 @@ public: // Until dtrace (via CTF) and LLDB can deal with distributed debug info, // FreeBSD defaults to standalone/full debug info. bool GetDefaultStandaloneDebug() const override { return true; } - llvm::DebuggerKind getDefaultDebuggerTuning() const override { - return llvm::DebuggerKind::LLDB; - } protected: Tool *buildAssembler() const override; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 84681053dd1a..5a2dbd388fc1 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -2069,6 +2069,16 @@ void Clang::AddHexagonTargetArgs(const ArgList &Args, CmdArgs.push_back("-machine-sink-split=0"); } +void Clang::AddWebAssemblyTargetArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + // Default to "hidden" visibility. + if (!Args.hasArg(options::OPT_fvisibility_EQ, + options::OPT_fvisibility_ms_compat)) { + CmdArgs.push_back("-fvisibility"); + CmdArgs.push_back("hidden"); + } +} + // Decode AArch64 features from string like +[no]featureA+[no]featureB+... static bool DecodeAArch64Features(const Driver &D, StringRef text, std::vector<const char *> &Features) { @@ -2970,7 +2980,7 @@ static void SplitDebugInfo(const ToolChain &TC, Compilation &C, const Tool &T, ExtractArgs.push_back(OutFile); const char *Exec = Args.MakeArgString(TC.GetProgramPath("objcopy")); - InputInfo II(Output.getFilename(), types::TY_Object, Output.getFilename()); + InputInfo II(types::TY_Object, Output.getFilename(), Output.getFilename()); // First extract the dwo sections. C.addCommand(llvm::make_unique<Command>(JA, T, Exec, ExtractArgs, II)); @@ -3253,8 +3263,9 @@ ParsePICArgs(const ToolChain &ToolChain, const llvm::Triple &Triple, // ToolChain.getTriple() and Triple? bool PIE = ToolChain.isPIEDefault(); bool PIC = PIE || ToolChain.isPICDefault(); - // The Darwin default to use PIC does not apply when using -static. - if (ToolChain.getTriple().isOSDarwin() && Args.hasArg(options::OPT_static)) + // The Darwin/MachO default to use PIC does not apply when using -static. + if (ToolChain.getTriple().isOSBinFormatMachO() && + Args.hasArg(options::OPT_static)) PIE = PIC = false; bool IsPICLevelTwo = PIC; @@ -4015,6 +4026,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, case llvm::Triple::hexagon: AddHexagonTargetArgs(Args, CmdArgs); break; + + case llvm::Triple::wasm32: + case llvm::Triple::wasm64: + AddWebAssemblyTargetArgs(Args, CmdArgs); + break; } // The 'g' groups options involve a somewhat intricate sequence of decisions @@ -4176,8 +4192,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-generate-type-units"); } - // CloudABI uses -ffunction-sections and -fdata-sections by default. - bool UseSeparateSections = Triple.getOS() == llvm::Triple::CloudABI; + // CloudABI and WebAssembly use -ffunction-sections and -fdata-sections by + // default. + bool UseSeparateSections = Triple.getOS() == llvm::Triple::CloudABI || + Triple.getArch() == llvm::Triple::wasm32 || + Triple.getArch() == llvm::Triple::wasm64; if (Args.hasFlag(options::OPT_ffunction_sections, options::OPT_fno_function_sections, UseSeparateSections)) { @@ -6040,8 +6059,7 @@ void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, // doesn't handle that so rather than warning about unused flags that are // actually used, we'll lie by omission instead. // FIXME: Stop lying and consume only the appropriate driver flags - for (const Arg *A : Args.filtered(options::OPT_W_Group)) - A->claim(); + Args.ClaimAllArgs(options::OPT_W_Group); CollectArgsForIntegratedAssembler(C, Args, CmdArgs, getToolChain().getDriver()); @@ -6078,6 +6096,12 @@ void gcc::Common::ConstructJob(Compilation &C, const JobAction &JA, for (const auto &A : Args) { if (forwardToGCC(A->getOption())) { + // It is unfortunate that we have to claim here, as this means + // we will basically never report anything interesting for + // platforms using a generic gcc, even if we are just using gcc + // to get to the assembler. + A->claim(); + // Don't forward any -g arguments to assembly steps. if (isa<AssembleJobAction>(JA) && A->getOption().matches(options::OPT_g_Group)) @@ -6088,11 +6112,6 @@ void gcc::Common::ConstructJob(Compilation &C, const JobAction &JA, A->getOption().matches(options::OPT_W_Group)) continue; - // It is unfortunate that we have to claim here, as this means - // we will basically never report anything interesting for - // platforms using a generic gcc, even if we are just using gcc - // to get to the assembler. - A->claim(); A->render(Args, CmdArgs); } } @@ -6502,10 +6521,6 @@ void amdgpu::Linker::ConstructJob(Compilation &C, const JobAction &JA, std::string Linker = getToolChain().GetProgramPath(getShortName()); ArgStringList CmdArgs; - CmdArgs.push_back("-flavor"); - CmdArgs.push_back("old-gnu"); - CmdArgs.push_back("-target"); - CmdArgs.push_back(Args.MakeArgString(getToolChain().getTripleString())); AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); @@ -6534,6 +6549,14 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA, ArgStringList CmdArgs; CmdArgs.push_back("-flavor"); CmdArgs.push_back("ld"); + + // Enable garbage collection of unused input sections by default, since code + // size is of particular importance. This is significantly facilitated by + // the enabling of -ffunction-sections and -fdata-sections in + // Clang::ConstructJob. + if (areOptimizationsEnabled(Args)) + CmdArgs.push_back("--gc-sections"); + AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); @@ -8965,7 +8988,7 @@ void nacltools::AssemblerARM::ConstructJob(Compilation &C, const JobAction &JA, const char *LinkingOutput) const { const toolchains::NaClToolChain &ToolChain = static_cast<const toolchains::NaClToolChain &>(getToolChain()); - InputInfo NaClMacros(ToolChain.GetNaClArmMacrosPath(), types::TY_PP_Asm, + InputInfo NaClMacros(types::TY_PP_Asm, ToolChain.GetNaClArmMacrosPath(), "nacl-arm-macros.s"); InputInfoList NewInputs; NewInputs.push_back(NaClMacros); diff --git a/lib/Driver/Tools.h b/lib/Driver/Tools.h index 168662f7e7fe..2b137f4a6d0b 100644 --- a/lib/Driver/Tools.h +++ b/lib/Driver/Tools.h @@ -82,6 +82,8 @@ private: llvm::opt::ArgStringList &CmdArgs) const; void AddHexagonTargetArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; + void AddWebAssemblyTargetArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const; enum RewriteKind { RK_None, RK_Fragile, RK_NonFragile }; @@ -238,7 +240,7 @@ namespace amdgpu { class LLVM_LIBRARY_VISIBILITY Linker : public GnuTool { public: - Linker(const ToolChain &TC) : GnuTool("amdgpu::Linker", "lld", TC) {} + Linker(const ToolChain &TC) : GnuTool("amdgpu::Linker", "ld.lld", TC) {} bool isLinkJob() const override { return true; } bool hasIntegratedCPP() const override { return false; } void ConstructJob(Compilation &C, const JobAction &JA, diff --git a/lib/Edit/RewriteObjCFoundationAPI.cpp b/lib/Edit/RewriteObjCFoundationAPI.cpp index 9f71168de8fc..482c0f6f8568 100644 --- a/lib/Edit/RewriteObjCFoundationAPI.cpp +++ b/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1077,6 +1077,9 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, case CK_BuiltinFnToFnPtr: case CK_ZeroToOCLEvent: return false; + + case CK_BooleanToSignedIntegral: + llvm_unreachable("OpenCL-specific cast in Objective-C?"); } } diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 8faab2869de6..11183355f73a 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -150,7 +150,12 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { if (Previous.is(tok::semi) && State.LineContainsContinuedForLoopSection) return true; if ((startsNextParameter(Current, Style) || Previous.is(tok::semi) || - (Previous.is(TT_TemplateCloser) && Current.is(TT_StartOfName)) || + (Previous.is(TT_TemplateCloser) && Current.is(TT_StartOfName) && + // FIXME: This is a temporary workaround for the case where clang-format + // sets BreakBeforeParameter to avoid bin packing and this creates a + // completely unnecessary line break after a template type that isn't + // line-wrapped. + (Previous.NestingLevel == 1 || Style.BinPackParameters)) || (Style.BreakBeforeTernaryOperators && Current.is(TT_ConditionalExpr) && Previous.isNot(tok::question)) || (!Style.BreakBeforeTernaryOperators && @@ -177,13 +182,15 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { return true; unsigned NewLineColumn = getNewLineColumn(State); - if (State.Column <= NewLineColumn) - return false; - if (Current.isMemberAccess() && - State.Column + getLengthToNextOperator(Current) > Style.ColumnLimit) + State.Column + getLengthToNextOperator(Current) > Style.ColumnLimit && + (State.Column > NewLineColumn || + Current.NestingLevel < State.StartOfLineLevel)) return true; + if (State.Column <= NewLineColumn) + return false; + if (Style.AlwaysBreakBeforeMultilineStrings && (NewLineColumn == State.FirstIndent + Style.ContinuationIndentWidth || Previous.is(tok::comma) || Current.NestingLevel < 2) && @@ -383,7 +390,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, State.Stack.back().LastSpace = State.Column; State.Stack.back().NestedBlockIndent = State.Column; } else if (!Current.isOneOf(tok::comment, tok::caret) && - (Previous.is(tok::comma) || + ((Previous.is(tok::comma) && + !Previous.is(TT_OverloadedOperator)) || (Previous.is(tok::colon) && Previous.is(TT_ObjCMethodExpr)))) { State.Stack.back().LastSpace = State.Column; } else if ((Previous.isOneOf(TT_BinaryOperator, TT_ConditionalExpr, @@ -860,7 +868,7 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State, (!SkipFirstExtraIndent && *I > prec::Assignment && !Current.isTrailingComment())) NewParenState.Indent += Style.ContinuationIndentWidth; - if ((Previous && !Previous->opensScope()) || *I > prec::Comma) + if ((Previous && !Previous->opensScope()) || *I != prec::Comma) NewParenState.BreakBeforeParameter = false; State.Stack.push_back(NewParenState); SkipFirstExtraIndent = false; @@ -906,8 +914,12 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, NewIndent = State.Stack.back().LastSpace + Style.ContinuationIndentWidth; } const FormatToken *NextNoComment = Current.getNextNonComment(); + bool EndsInComma = Current.MatchingParen && + Current.MatchingParen->Previous && + Current.MatchingParen->Previous->is(tok::comma); AvoidBinPacking = - Current.isOneOf(TT_ArrayInitializerLSquare, TT_DictLiteral) || + (Current.is(TT_ArrayInitializerLSquare) && EndsInComma) || + Current.is(TT_DictLiteral) || Style.Language == FormatStyle::LK_Proto || !Style.BinPackArguments || (NextNoComment && NextNoComment->is(TT_DesignatedInitializerPeriod)); if (Current.ParameterCount > 1) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 5068fca5c44d..2689368da513 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -583,6 +583,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { GoogleStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Inline; GoogleStyle.AlwaysBreakBeforeMultilineStrings = false; GoogleStyle.BreakBeforeTernaryOperators = false; + GoogleStyle.CommentPragmas = "@(export|visibility) {"; GoogleStyle.MaxEmptyLinesToKeep = 3; GoogleStyle.SpacesInContainerLiterals = false; } else if (Language == FormatStyle::LK_Proto) { @@ -1238,6 +1239,8 @@ private: FormatTok->Type = TT_ImplicitStringLiteral; break; } + if (FormatTok->Type == TT_ImplicitStringLiteral) + break; } if (FormatTok->is(TT_ImplicitStringLiteral)) @@ -1901,8 +1904,9 @@ tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), new DiagnosticOptions); SourceManager SourceMgr(Diagnostics, Files); - InMemoryFileSystem->addFile(FileName, 0, - llvm::MemoryBuffer::getMemBuffer(Code, FileName)); + InMemoryFileSystem->addFile( + FileName, 0, llvm::MemoryBuffer::getMemBuffer( + Code, FileName, /*RequiresNullTerminator=*/false)); FileID ID = SourceMgr.createFileID(Files.getFile(FileName), SourceLocation(), clang::SrcMgr::C_User); SourceLocation StartOfFile = SourceMgr.getLocForStartOfFile(ID); diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index caff1312f300..8fbb43b7996d 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -119,7 +119,9 @@ private: } } - if (Left->Previous && + if (Left->is(TT_OverloadedOperatorLParen)) { + Contexts.back().IsExpression = false; + } else if (Left->Previous && (Left->Previous->isOneOf(tok::kw_static_assert, tok::kw_decltype, tok::kw_if, tok::kw_while, tok::l_paren, tok::comma) || @@ -132,9 +134,7 @@ private: // This is a parameter list of a lambda expression. Contexts.back().IsExpression = false; } else if (Line.InPPDirective && - (!Left->Previous || - !Left->Previous->isOneOf(tok::identifier, - TT_OverloadedOperator))) { + (!Left->Previous || !Left->Previous->is(tok::identifier))) { Contexts.back().IsExpression = true; } else if (Contexts[Contexts.size() - 2].CaretFound) { // This is the parameter list of an ObjC block. @@ -199,6 +199,18 @@ private: Left->MatchingParen = CurrentToken; CurrentToken->MatchingParen = Left; + if (CurrentToken->Next && CurrentToken->Next->is(tok::l_brace) && + Left->Previous && Left->Previous->is(tok::l_paren)) { + // Detect the case where macros are used to generate lambdas or + // function bodies, e.g.: + // auto my_lambda = MARCO((Type *type, int i) { .. body .. }); + for (FormatToken *Tok = Left; Tok != CurrentToken; Tok = Tok->Next) { + if (Tok->is(TT_BinaryOperator) && + Tok->isOneOf(tok::star, tok::amp, tok::ampamp)) + Tok->Type = TT_PointerOrReference; + } + } + if (StartsObjCMethodExpr) { CurrentToken->Type = TT_ObjCMethodExpr; if (Contexts.back().FirstObjCSelectorName) { @@ -568,7 +580,8 @@ private: if (CurrentToken->isOneOf(tok::star, tok::amp)) CurrentToken->Type = TT_PointerOrReference; consumeToken(); - if (CurrentToken && CurrentToken->Previous->is(TT_BinaryOperator)) + if (CurrentToken && + CurrentToken->Previous->isOneOf(TT_BinaryOperator, tok::comma)) CurrentToken->Previous->Type = TT_OverloadedOperator; } if (CurrentToken) { @@ -1713,7 +1726,7 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, if (Right.is(Keywords.kw_function) && Left.isNot(tok::comma)) return 100; if (Left.is(TT_JsTypeColon)) - return 100; + return 35; } if (Left.is(tok::comma) || (Right.is(tok::identifier) && Right.Next && @@ -2058,14 +2071,14 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, if (Right.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow) || Left.isOneOf(TT_TrailingReturnArrow, TT_LambdaArrow)) return true; + if (Right.is(TT_OverloadedOperatorLParen)) + return Style.SpaceBeforeParens == FormatStyle::SBPO_Always; if (Left.is(tok::comma)) return true; if (Right.is(tok::comma)) return false; if (Right.isOneOf(TT_CtorInitializerColon, TT_ObjCBlockLParen)) return true; - if (Right.is(TT_OverloadedOperatorLParen)) - return Style.SpaceBeforeParens == FormatStyle::SBPO_Always; if (Right.is(tok::colon)) { if (Line.First->isOneOf(tok::kw_case, tok::kw_default) || !Right.getNextNonComment() || Right.getNextNonComment()->is(tok::semi)) diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index 94b849881941..7b8f6e652416 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -315,6 +315,7 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) { // definitions, too. unsigned StoredPosition = Tokens->getPosition(); FormatToken *Tok = FormatTok; + const FormatToken *PrevTok = getPreviousToken(); // Keep a stack of positions of lbrace tokens. We will // update information about whether an lbrace starts a // braced init list or a different block during the loop. @@ -331,47 +332,53 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) { switch (Tok->Tok.getKind()) { case tok::l_brace: - Tok->BlockKind = BK_Unknown; + if (Style.Language == FormatStyle::LK_JavaScript && PrevTok && + PrevTok->is(tok::colon)) + // In TypeScript's TypeMemberLists, there can be semicolons between the + // individual members. + Tok->BlockKind = BK_BracedInit; + else + Tok->BlockKind = BK_Unknown; LBraceStack.push_back(Tok); break; case tok::r_brace: - if (!LBraceStack.empty()) { - if (LBraceStack.back()->BlockKind == BK_Unknown) { - bool ProbablyBracedList = false; - if (Style.Language == FormatStyle::LK_Proto) { - ProbablyBracedList = NextTok->isOneOf(tok::comma, tok::r_square); - } else { - // Using OriginalColumn to distinguish between ObjC methods and - // binary operators is a bit hacky. - bool NextIsObjCMethod = NextTok->isOneOf(tok::plus, tok::minus) && - NextTok->OriginalColumn == 0; - - // If there is a comma, semicolon or right paren after the closing - // brace, we assume this is a braced initializer list. Note that - // regardless how we mark inner braces here, we will overwrite the - // BlockKind later if we parse a braced list (where all blocks - // inside are by default braced lists), or when we explicitly detect - // blocks (for example while parsing lambdas). - // - // We exclude + and - as they can be ObjC visibility modifiers. - ProbablyBracedList = - NextTok->isOneOf(tok::comma, tok::period, tok::colon, - tok::r_paren, tok::r_square, tok::l_brace, - tok::l_square, tok::l_paren, tok::ellipsis) || - (NextTok->is(tok::semi) && - (!ExpectClassBody || LBraceStack.size() != 1)) || - (NextTok->isBinaryOperator() && !NextIsObjCMethod); - } - if (ProbablyBracedList) { - Tok->BlockKind = BK_BracedInit; - LBraceStack.back()->BlockKind = BK_BracedInit; - } else { - Tok->BlockKind = BK_Block; - LBraceStack.back()->BlockKind = BK_Block; - } + if (LBraceStack.empty()) + break; + if (LBraceStack.back()->BlockKind == BK_Unknown) { + bool ProbablyBracedList = false; + if (Style.Language == FormatStyle::LK_Proto) { + ProbablyBracedList = NextTok->isOneOf(tok::comma, tok::r_square); + } else { + // Using OriginalColumn to distinguish between ObjC methods and + // binary operators is a bit hacky. + bool NextIsObjCMethod = NextTok->isOneOf(tok::plus, tok::minus) && + NextTok->OriginalColumn == 0; + + // If there is a comma, semicolon or right paren after the closing + // brace, we assume this is a braced initializer list. Note that + // regardless how we mark inner braces here, we will overwrite the + // BlockKind later if we parse a braced list (where all blocks + // inside are by default braced lists), or when we explicitly detect + // blocks (for example while parsing lambdas). + // + // We exclude + and - as they can be ObjC visibility modifiers. + ProbablyBracedList = + NextTok->isOneOf(tok::comma, tok::period, tok::colon, + tok::r_paren, tok::r_square, tok::l_brace, + tok::l_square, tok::l_paren, tok::ellipsis) || + (NextTok->is(tok::semi) && + (!ExpectClassBody || LBraceStack.size() != 1)) || + (NextTok->isBinaryOperator() && !NextIsObjCMethod); + } + if (ProbablyBracedList) { + Tok->BlockKind = BK_BracedInit; + LBraceStack.back()->BlockKind = BK_BracedInit; + } else { + Tok->BlockKind = BK_Block; + LBraceStack.back()->BlockKind = BK_Block; } - LBraceStack.pop_back(); } + LBraceStack.pop_back(); break; case tok::at: case tok::semi: @@ -381,14 +388,16 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) { case tok::kw_switch: case tok::kw_try: case tok::kw___try: - if (!LBraceStack.empty()) + if (!LBraceStack.empty() && LBraceStack.back()->BlockKind == BK_Unknown) LBraceStack.back()->BlockKind = BK_Block; break; default: break; } + PrevTok = Tok; Tok = NextTok; } while (Tok->Tok.isNot(tok::eof) && !LBraceStack.empty()); + // Assume other blocks for all unclosed opening braces. for (unsigned i = 0, e = LBraceStack.size(); i != e; ++i) { if (LBraceStack[i]->BlockKind == BK_Unknown) @@ -841,6 +850,8 @@ void UnwrappedLineParser::parseStructuralElement() { // This does not apply for Java and JavaScript. if (Style.Language == FormatStyle::LK_Java || Style.Language == FormatStyle::LK_JavaScript) { + if (FormatTok->is(tok::semi)) + nextToken(); addUnwrappedLine(); return; } @@ -986,13 +997,11 @@ bool UnwrappedLineParser::tryToParseLambda() { nextToken(); return false; } - // FIXME: This is a dirty way to access the previous token. Find a better - // solution. - if (!Line->Tokens.empty() && - (Line->Tokens.back().Tok->isOneOf(tok::identifier, tok::kw_operator, - tok::kw_new, tok::kw_delete) || - Line->Tokens.back().Tok->closesScope() || - Line->Tokens.back().Tok->isSimpleTypeSpecifier())) { + const FormatToken* Previous = getPreviousToken(); + if (Previous && + (Previous->isOneOf(tok::identifier, tok::kw_operator, tok::kw_new, + tok::kw_delete) || + Previous->closesScope() || Previous->isSimpleTypeSpecifier())) { nextToken(); return false; } @@ -1174,6 +1183,14 @@ bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons) { nextToken(); return !HasError; case tok::semi: + // JavaScript (or more precisely TypeScript) can have semicolons in braced + // lists (in so-called TypeMemberLists). Thus, the semicolon cannot be + // used for error recovery if we have otherwise determined that this is + // a braced list. + if (Style.Language == FormatStyle::LK_JavaScript) { + nextToken(); + break; + } HasError = true; if (!ContinueOnSemicolons) return !HasError; @@ -1792,18 +1809,22 @@ void UnwrappedLineParser::parseJavaScriptEs6ImportExport() { return; } + // Consume the "abstract" in "export abstract class". + if (FormatTok->is(Keywords.kw_abstract)) + nextToken(); + if (FormatTok->isOneOf(tok::kw_const, tok::kw_class, tok::kw_enum, - Keywords.kw_let, Keywords.kw_var)) + Keywords.kw_interface, Keywords.kw_let, + Keywords.kw_var)) return; // Fall through to parsing the corresponding structure. - if (FormatTok->is(tok::l_brace)) { - FormatTok->BlockKind = BK_Block; - parseBracedList(); - } - - while (!eof() && FormatTok->isNot(tok::semi) && - FormatTok->isNot(tok::l_brace)) { - nextToken(); + while (!eof() && FormatTok->isNot(tok::semi)) { + if (FormatTok->is(tok::l_brace)) { + FormatTok->BlockKind = BK_Block; + parseBracedList(); + } else { + nextToken(); + } } } @@ -1877,6 +1898,14 @@ void UnwrappedLineParser::nextToken() { readToken(); } +const FormatToken *UnwrappedLineParser::getPreviousToken() { + // FIXME: This is a dirty way to access the previous token. Find a better + // solution. + if (!Line || Line->Tokens.empty()) + return nullptr; + return Line->Tokens.back().Tok; +} + void UnwrappedLineParser::readToken() { bool CommentsInCurrentLine = true; do { diff --git a/lib/Format/UnwrappedLineParser.h b/lib/Format/UnwrappedLineParser.h index a13c03f94086..6d40ab4f3120 100644 --- a/lib/Format/UnwrappedLineParser.h +++ b/lib/Format/UnwrappedLineParser.h @@ -110,6 +110,7 @@ private: void addUnwrappedLine(); bool eof() const; void nextToken(); + const FormatToken *getPreviousToken(); void readToken(); void flushComments(bool NewlineBeforeNext); void pushToken(FormatToken *Tok); diff --git a/lib/Format/WhitespaceManager.cpp b/lib/Format/WhitespaceManager.cpp index 725f05bcd8fc..d6e6ed2c2baa 100644 --- a/lib/Format/WhitespaceManager.cpp +++ b/lib/Format/WhitespaceManager.cpp @@ -30,7 +30,7 @@ WhitespaceManager::Change::Change( unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn, unsigned NewlinesBefore, StringRef PreviousLinePostfix, StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective, - bool IsStartOfDeclName) + bool IsStartOfDeclName, bool IsInsideToken) : CreateReplacement(CreateReplacement), OriginalWhitespaceRange(OriginalWhitespaceRange), StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore), @@ -38,8 +38,8 @@ WhitespaceManager::Change::Change( CurrentLinePrefix(CurrentLinePrefix), Kind(Kind), ContinuesPPDirective(ContinuesPPDirective), IsStartOfDeclName(IsStartOfDeclName), IndentLevel(IndentLevel), - Spaces(Spaces), IsTrailingComment(false), TokenLength(0), - PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0), + Spaces(Spaces), IsInsideToken(IsInsideToken), IsTrailingComment(false), + TokenLength(0), PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0), StartOfBlockComment(nullptr), IndentationOffset(0) {} void WhitespaceManager::reset() { @@ -55,20 +55,23 @@ void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines, return; Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue; Changes.push_back( - Change(true, Tok.WhitespaceRange, IndentLevel, Spaces, StartOfTokenColumn, - Newlines, "", "", Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst, - Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName))); + Change(/*CreateReplacement=*/true, Tok.WhitespaceRange, IndentLevel, + Spaces, StartOfTokenColumn, Newlines, "", "", Tok.Tok.getKind(), + InPPDirective && !Tok.IsFirst, + Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName), + /*IsInsideToken=*/false)); } void WhitespaceManager::addUntouchableToken(const FormatToken &Tok, bool InPPDirective) { if (Tok.Finalized) return; - Changes.push_back( - Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0, - /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, "", "", - Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst, - Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName))); + Changes.push_back(Change( + /*CreateReplacement=*/false, Tok.WhitespaceRange, /*IndentLevel=*/0, + /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, "", "", + Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst, + Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName), + /*IsInsideToken=*/false)); } void WhitespaceManager::replaceWhitespaceInToken( @@ -81,15 +84,10 @@ void WhitespaceManager::replaceWhitespaceInToken( Changes.push_back(Change( true, SourceRange(Start, Start.getLocWithOffset(ReplaceChars)), IndentLevel, Spaces, std::max(0, Spaces), Newlines, PreviousPostfix, - CurrentPrefix, - // If we don't add a newline this change doesn't start a comment. Thus, - // when we align line comments, we don't need to treat this change as one. - // FIXME: We still need to take this change in account to properly - // calculate the new length of the comment and to calculate the changes - // for which to do the alignment when aligning comments. - Tok.is(TT_LineComment) && Newlines > 0 ? tok::comment : tok::unknown, + CurrentPrefix, Tok.is(TT_LineComment) ? tok::comment : tok::unknown, InPPDirective && !Tok.IsFirst, - Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName))); + Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName), + /*IsInsideToken=*/Newlines == 0)); } const tooling::Replacements &WhitespaceManager::generateReplacements() { @@ -109,6 +107,7 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() { void WhitespaceManager::calculateLineBreakInformation() { Changes[0].PreviousEndOfTokenColumn = 0; + Change *LastOutsideTokenChange = &Changes[0]; for (unsigned i = 1, e = Changes.size(); i != e; ++i) { unsigned OriginalWhitespaceStart = SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin()); @@ -119,11 +118,20 @@ void WhitespaceManager::calculateLineBreakInformation() { Changes[i].PreviousLinePostfix.size() + Changes[i - 1].CurrentLinePrefix.size(); + // If there are multiple changes in this token, sum up all the changes until + // the end of the line. + if (Changes[i - 1].IsInsideToken) + LastOutsideTokenChange->TokenLength += + Changes[i - 1].TokenLength + Changes[i - 1].Spaces; + else + LastOutsideTokenChange = &Changes[i - 1]; + Changes[i].PreviousEndOfTokenColumn = Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength; Changes[i - 1].IsTrailingComment = - (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof) && + (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof || + (Changes[i].IsInsideToken && Changes[i].Kind == tok::comment)) && Changes[i - 1].Kind == tok::comment; } // FIXME: The last token is currently not always an eof token; in those @@ -133,6 +141,10 @@ void WhitespaceManager::calculateLineBreakInformation() { const WhitespaceManager::Change *LastBlockComment = nullptr; for (auto &Change : Changes) { + // Reset the IsTrailingComment flag for changes inside of trailing comments + // so they don't get realigned later. + if (Change.IsInsideToken) + Change.IsTrailingComment = false; Change.StartOfBlockComment = nullptr; Change.IndentationOffset = 0; if (Change.Kind == tok::comment) { @@ -342,6 +354,12 @@ void WhitespaceManager::alignTrailingComments() { unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn; unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength; + + // If we don't create a replacement for this change, we have to consider + // it to be immovable. + if (!Changes[i].CreateReplacement) + ChangeMaxColumn = ChangeMinColumn; + if (i + 1 != e && Changes[i + 1].ContinuesPPDirective) ChangeMaxColumn -= 2; // If this comment follows an } in column 0, it probably documents the diff --git a/lib/Format/WhitespaceManager.h b/lib/Format/WhitespaceManager.h index f83971b4add6..9ca9db6f7488 100644 --- a/lib/Format/WhitespaceManager.h +++ b/lib/Format/WhitespaceManager.h @@ -109,7 +109,8 @@ public: unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn, unsigned NewlinesBefore, StringRef PreviousLinePostfix, StringRef CurrentLinePrefix, tok::TokenKind Kind, - bool ContinuesPPDirective, bool IsStartOfDeclName); + bool ContinuesPPDirective, bool IsStartOfDeclName, + bool IsInsideToken); bool CreateReplacement; // Changes might be in the middle of a token, so we cannot just keep the @@ -139,6 +140,10 @@ public: // comments. Uncompensated negative offset is truncated to 0. int Spaces; + // If this change is inside of a token but not at the start of the token or + // directly after a newline. + bool IsInsideToken; + // \c IsTrailingComment, \c TokenLength, \c PreviousEndOfTokenColumn and // \c EscapedNewlineColumn will be calculated in // \c calculateLineBreakInformation. diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 3a32f476566c..237a44704096 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1,4 +1,4 @@ -//===--- +//===--- CompilerInvocation.cpp -------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -2202,8 +2202,11 @@ std::string CompilerInvocation::getModuleHash() const { code = hash_combine(code, I->first, I->second); } - // Extend the signature with the sysroot. - code = hash_combine(code, hsOpts.Sysroot, hsOpts.UseBuiltinIncludes, + // Extend the signature with the sysroot and other header search options. + code = hash_combine(code, hsOpts.Sysroot, + hsOpts.ModuleFormat, + hsOpts.UseDebugInfo, + hsOpts.UseBuiltinIncludes, hsOpts.UseStandardSystemIncludes, hsOpts.UseStandardCXXIncludes, hsOpts.UseLibcxx); diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index d6c88d20fc2a..407ccea2e7d1 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -187,15 +187,17 @@ collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr, return std::error_code(); // Add includes for each of these headers. - for (Module::Header &H : Module->Headers[Module::HK_Normal]) { - Module->addTopHeader(H.Entry); - // Use the path as specified in the module map file. We'll look for this - // file relative to the module build directory (the directory containing - // the module map file) so this will find the same file that we found - // while parsing the module map. - if (std::error_code Err = addHeaderInclude(H.NameAsWritten, Includes, - LangOpts, Module->IsExternC)) - return Err; + for (auto HK : {Module::HK_Normal, Module::HK_Private}) { + for (Module::Header &H : Module->Headers[HK]) { + Module->addTopHeader(H.Entry); + // Use the path as specified in the module map file. We'll look for this + // file relative to the module build directory (the directory containing + // the module map file) so this will find the same file that we found + // while parsing the module map. + if (std::error_code Err = addHeaderInclude(H.NameAsWritten, Includes, + LangOpts, Module->IsExternC)) + return Err; + } } // Note that Module->PrivateHeaders will not be a TopHeader. diff --git a/lib/Frontend/MultiplexConsumer.cpp b/lib/Frontend/MultiplexConsumer.cpp index 12c85240bd75..f8b73e9034b3 100644 --- a/lib/Frontend/MultiplexConsumer.cpp +++ b/lib/Frontend/MultiplexConsumer.cpp @@ -119,6 +119,7 @@ public: const FunctionDecl *Delete) override; void CompletedImplicitDefinition(const FunctionDecl *D) override; void StaticDataMemberInstantiated(const VarDecl *D) override; + void DefaultArgumentInstantiated(const ParmVarDecl *D) override; void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, const ObjCInterfaceDecl *IFD) override; void FunctionDefinitionInstantiated(const FunctionDecl *D) override; @@ -193,6 +194,11 @@ void MultiplexASTMutationListener::StaticDataMemberInstantiated( for (size_t i = 0, e = Listeners.size(); i != e; ++i) Listeners[i]->StaticDataMemberInstantiated(D); } +void MultiplexASTMutationListener::DefaultArgumentInstantiated( + const ParmVarDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DefaultArgumentInstantiated(D); +} void MultiplexASTMutationListener::AddedObjCCategoryToInterface( const ObjCCategoryDecl *CatD, const ObjCInterfaceDecl *IFD) { diff --git a/lib/Headers/altivec.h b/lib/Headers/altivec.h index dc0dcbc7385c..a5b4f7434d1c 100644 --- a/lib/Headers/altivec.h +++ b/lib/Headers/altivec.h @@ -1891,6 +1891,22 @@ static vector float __ATTRS_o_ai vec_ctf(vector unsigned int __a, int __b) { return __builtin_altivec_vcfux((vector int)__a, __b); } +#ifdef __VSX__ +static vector double __ATTRS_o_ai vec_ctf(vector unsigned long long __a, + int __b) { + vector double __ret = __builtin_convertvector(__a, vector double); + __ret *= (vector double)(vector unsigned long long)((0x3ffULL - __b) << 52); + return __ret; +} + +static vector double __ATTRS_o_ai vec_ctf(vector signed long long __a, + int __b) { + vector double __ret = __builtin_convertvector(__a, vector double); + __ret *= (vector double)(vector unsigned long long)((0x3ffULL - __b) << 52); + return __ret; +} +#endif + /* vec_vcfsx */ static vector float __attribute__((__always_inline__)) @@ -1907,11 +1923,18 @@ vec_vcfux(vector unsigned int __a, int __b) { /* vec_cts */ -static vector int __attribute__((__always_inline__)) -vec_cts(vector float __a, int __b) { +static vector int __ATTRS_o_ai vec_cts(vector float __a, int __b) { return __builtin_altivec_vctsxs(__a, __b); } +#ifdef __VSX__ +static vector signed long long __ATTRS_o_ai vec_cts(vector double __a, + int __b) { + __a *= (vector double)(vector unsigned long long)((0x3ffULL + __b) << 52); + return __builtin_convertvector(__a, vector signed long long); +} +#endif + /* vec_vctsxs */ static vector int __attribute__((__always_inline__)) @@ -1921,11 +1944,18 @@ vec_vctsxs(vector float __a, int __b) { /* vec_ctu */ -static vector unsigned int __attribute__((__always_inline__)) -vec_ctu(vector float __a, int __b) { +static vector unsigned int __ATTRS_o_ai vec_ctu(vector float __a, int __b) { return __builtin_altivec_vctuxs(__a, __b); } +#ifdef __VSX__ +static vector unsigned long long __ATTRS_o_ai vec_ctu(vector double __a, + int __b) { + __a *= (vector double)(vector unsigned long long)((0x3ffULL + __b) << 52); + return __builtin_convertvector(__a, vector unsigned long long); +} +#endif + /* vec_vctuxs */ static vector unsigned int __attribute__((__always_inline__)) diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp index 8a686a7f3d74..2d005dd2e1f0 100644 --- a/lib/Lex/HeaderSearch.cpp +++ b/lib/Lex/HeaderSearch.cpp @@ -153,8 +153,7 @@ std::string HeaderSearch::getModuleFileName(StringRef ModuleName, auto FileName = llvm::sys::path::filename(ModuleMapPath); llvm::hash_code Hash = - llvm::hash_combine(DirName.lower(), FileName.lower(), - HSOpts->ModuleFormat, HSOpts->UseDebugInfo); + llvm::hash_combine(DirName.lower(), FileName.lower()); SmallString<128> HashStr; llvm::APInt(64, size_t(Hash)).toStringUnsigned(HashStr, /*Radix*/36); diff --git a/lib/Lex/LiteralSupport.cpp b/lib/Lex/LiteralSupport.cpp index 1e7858af8948..5b1c49344e8d 100644 --- a/lib/Lex/LiteralSupport.cpp +++ b/lib/Lex/LiteralSupport.cpp @@ -983,6 +983,7 @@ NumericLiteralParser::GetFloatValue(llvm::APFloat &Result) { /// u' c-char-sequence ' /// U' c-char-sequence ' /// L' c-char-sequence ' +/// u8' c-char-sequence ' [C++1z lex.ccon] /// c-char-sequence: /// c-char /// c-char-sequence c-char diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp index 3134790ccb90..afb41a240776 100644 --- a/lib/Lex/Pragma.cpp +++ b/lib/Lex/Pragma.cpp @@ -876,6 +876,22 @@ struct PragmaDebugHandler : public PragmaHandler { Crasher.setKind(tok::annot_pragma_parser_crash); Crasher.setAnnotationRange(SourceRange(Tok.getLocation())); PP.EnterToken(Crasher); + } else if (II->isStr("dump")) { + Token Identifier; + PP.LexUnexpandedToken(Identifier); + if (auto *DumpII = Identifier.getIdentifierInfo()) { + Token DumpAnnot; + DumpAnnot.startToken(); + DumpAnnot.setKind(tok::annot_pragma_dump); + DumpAnnot.setAnnotationRange( + SourceRange(Tok.getLocation(), Identifier.getLocation())); + DumpAnnot.setAnnotationValue(DumpII); + PP.DiscardUntilEndOfDirective(); + PP.EnterToken(DumpAnnot); + } else { + PP.Diag(Identifier, diag::warn_pragma_debug_missing_argument) + << II->getName(); + } } else if (II->isStr("llvm_fatal_error")) { llvm::report_fatal_error("#pragma clang __debug llvm_fatal_error"); } else if (II->isStr("llvm_unreachable")) { @@ -887,7 +903,8 @@ struct PragmaDebugHandler : public PragmaHandler { if (MacroII) PP.dumpMacroInfo(MacroII); else - PP.Diag(MacroName, diag::warn_pragma_diagnostic_invalid); + PP.Diag(MacroName, diag::warn_pragma_debug_missing_argument) + << II->getName(); } else if (II->isStr("overflow_stack")) { DebugOverflowStack(); } else if (II->isStr("handle_crash")) { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index e69bb2745c43..c64b97d01b9a 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3326,6 +3326,15 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, case tok::kw___bool: isInvalid = DS.SetTypeAltiVecBool(true, Loc, PrevSpec, DiagID, Policy); break; + case tok::kw_pipe: + if (!getLangOpts().OpenCL || (getLangOpts().OpenCLVersion < 200)) { + // OpenCL 2.0 defined this keyword. OpenCL 1.2 and earlier should + // support the "pipe" word as identifier. + Tok.getIdentifierInfo()->revertTokenIDToIdentifier(); + goto DoneWithDeclSpec; + } + isInvalid = DS.SetTypePipe(true, Loc, PrevSpec, DiagID, Policy); + break; case tok::kw___unknown_anytype: isInvalid = DS.SetTypeSpecType(TST_unknown_anytype, Loc, PrevSpec, DiagID, Policy); @@ -4401,6 +4410,9 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { switch (Tok.getKind()) { default: return false; + case tok::kw_pipe: + return getLangOpts().OpenCL && (getLangOpts().OpenCLVersion >= 200); + case tok::identifier: // foo::bar // Unfortunate hack to support "Class.factoryMethod" notation. if (getLangOpts().ObjC1 && NextToken().is(tok::period)) @@ -4847,6 +4859,9 @@ static bool isPtrOperatorToken(tok::TokenKind Kind, const LangOptions &Lang, if (Kind == tok::star || Kind == tok::caret) return true; + if ((Kind == tok::kw_pipe) && Lang.OpenCL && (Lang.OpenCLVersion >= 200)) + return true; + if (!Lang.CPlusPlus) return false; @@ -4865,6 +4880,17 @@ static bool isPtrOperatorToken(tok::TokenKind Kind, const LangOptions &Lang, return false; } +// Indicates whether the given declarator is a pipe declarator. +static bool isPipeDeclerator(const Declarator &D) { + const unsigned NumTypes = D.getNumTypeObjects(); + + for (unsigned Idx = 0; Idx != NumTypes; ++Idx) + if (DeclaratorChunk::Pipe == D.getTypeObject(Idx).Kind) + return true; + + return false; +} + /// ParseDeclaratorInternal - Parse a C or C++ declarator. The direct-declarator /// is parsed by the function passed to it. Pass null, and the direct-declarator /// isn't parsed at all, making this function effectively parse the C++ @@ -4941,6 +4967,15 @@ void Parser::ParseDeclaratorInternal(Declarator &D, } tok::TokenKind Kind = Tok.getKind(); + + if (D.getDeclSpec().isTypeSpecPipe() && !isPipeDeclerator(D)) { + DeclSpec &DS = D.getMutableDeclSpec(); + + D.AddTypeInfo( + DeclaratorChunk::getPipe(DS.getTypeQualifiers(), DS.getPipeLoc()), + DS.getAttributes(), SourceLocation()); + } + // Not a pointer, C++ reference, or block. if (!isPtrOperatorToken(Kind, getLangOpts(), D.getContext())) { if (DirectDeclParser) @@ -6092,6 +6127,7 @@ void Parser::ParseMisplacedBracketDeclarator(Declarator &D) { case DeclaratorChunk::Reference: case DeclaratorChunk::BlockPointer: case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: NeedParens = true; break; case DeclaratorChunk::Array: diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index a4de9751f9a0..3f22ad4ddaba 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -3363,7 +3363,8 @@ Parser::tryParseExceptionSpecification(bool Delayed, ConsumeAndStoreUntil(tok::r_paren, *ExceptionSpecTokens, /*StopAtSemi=*/true, /*ConsumeFinalToken=*/true); - SpecificationRange.setEnd(Tok.getLocation()); + SpecificationRange.setEnd(ExceptionSpecTokens->back().getLocation()); + return EST_Unparsed; } diff --git a/lib/Parse/ParseOpenMP.cpp b/lib/Parse/ParseOpenMP.cpp index 078f4c388705..a08db5490fa9 100644 --- a/lib/Parse/ParseOpenMP.cpp +++ b/lib/Parse/ParseOpenMP.cpp @@ -165,8 +165,8 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() { /// 'distribute' /// annot_pragma_openmp_end /// -StmtResult -Parser::ParseOpenMPDeclarativeOrExecutableDirective(bool StandAloneAllowed) { +StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective( + AllowedContsructsKind Allowed) { assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!"); ParenBraceBracketBalancer BalancerRAIIObj(*this); SmallVector<Expr *, 5> Identifiers; @@ -186,6 +186,10 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(bool StandAloneAllowed) { switch (DKind) { case OMPD_threadprivate: + if (Allowed != ACK_Any) { + Diag(Tok, diag::err_omp_immediate_directive) + << getOpenMPDirectiveName(DKind) << 0; + } ConsumeToken(); if (!ParseOpenMPSimpleVarList(OMPD_threadprivate, Identifiers, false)) { // The last seen token is annot_pragma_openmp_end - need to check for @@ -213,7 +217,7 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(bool StandAloneAllowed) { case OMPD_taskwait: case OMPD_cancellation_point: case OMPD_cancel: - if (!StandAloneAllowed) { + if (Allowed == ACK_StatementsOpenMPNonStandalone) { Diag(Tok, diag::err_omp_immediate_directive) << getOpenMPDirectiveName(DKind) << 0; } @@ -299,7 +303,7 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(bool StandAloneAllowed) { // If the depend clause is specified, the ordered construct is a stand-alone // directive. if (DKind == OMPD_ordered && FirstClauses[OMPC_depend].getInt()) { - if (!StandAloneAllowed) { + if (Allowed == ACK_StatementsOpenMPNonStandalone) { Diag(Loc, diag::err_omp_immediate_directive) << getOpenMPDirectiveName(DKind) << 1 << getOpenMPClauseName(OMPC_depend); diff --git a/lib/Parse/ParsePragma.cpp b/lib/Parse/ParsePragma.cpp index 4430eb8d03da..bc70942851e2 100644 --- a/lib/Parse/ParsePragma.cpp +++ b/lib/Parse/ParsePragma.cpp @@ -377,6 +377,14 @@ void Parser::HandlePragmaAlign() { Actions.ActOnPragmaOptionsAlign(Kind, PragmaLoc); } +void Parser::HandlePragmaDump() { + assert(Tok.is(tok::annot_pragma_dump)); + IdentifierInfo *II = + reinterpret_cast<IdentifierInfo *>(Tok.getAnnotationValue()); + Actions.ActOnPragmaDump(getCurScope(), Tok.getLocation(), II); + ConsumeToken(); +} + void Parser::HandlePragmaWeak() { assert(Tok.is(tok::annot_pragma_weak)); SourceLocation PragmaLoc = ConsumeToken(); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 717bcff0c168..edf0dda7df8c 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -32,14 +32,18 @@ using namespace clang; /// \brief Parse a standalone statement (for instance, as the body of an 'if', /// 'while', or 'for'). -StmtResult Parser::ParseStatement(SourceLocation *TrailingElseLoc) { +StmtResult Parser::ParseStatement(SourceLocation *TrailingElseLoc, + bool AllowOpenMPStandalone) { StmtResult Res; // We may get back a null statement if we found a #pragma. Keep going until // we get an actual statement. do { StmtVector Stmts; - Res = ParseStatementOrDeclaration(Stmts, true, TrailingElseLoc); + Res = ParseStatementOrDeclaration( + Stmts, AllowOpenMPStandalone ? ACK_StatementsOpenMPAnyExecutable + : ACK_StatementsOpenMPNonStandalone, + TrailingElseLoc); } while (!Res.isInvalid() && !Res.get()); return Res; @@ -95,7 +99,8 @@ StmtResult Parser::ParseStatement(SourceLocation *TrailingElseLoc) { /// [OBC] '@' 'throw' ';' /// StmtResult -Parser::ParseStatementOrDeclaration(StmtVector &Stmts, bool OnlyStatement, +Parser::ParseStatementOrDeclaration(StmtVector &Stmts, + AllowedContsructsKind Allowed, SourceLocation *TrailingElseLoc) { ParenBraceBracketBalancer BalancerRAIIObj(*this); @@ -103,8 +108,8 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts, bool OnlyStatement, ParsedAttributesWithRange Attrs(AttrFactory); MaybeParseCXX11Attributes(Attrs, nullptr, /*MightBeObjCMessageSend*/ true); - StmtResult Res = ParseStatementOrDeclarationAfterAttributes(Stmts, - OnlyStatement, TrailingElseLoc, Attrs); + StmtResult Res = ParseStatementOrDeclarationAfterAttributes( + Stmts, Allowed, TrailingElseLoc, Attrs); assert((Attrs.empty() || Res.isInvalid() || Res.isUsable()) && "attributes on empty statement"); @@ -146,7 +151,7 @@ private: StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(StmtVector &Stmts, - bool OnlyStatement, SourceLocation *TrailingElseLoc, + AllowedContsructsKind Allowed, SourceLocation *TrailingElseLoc, ParsedAttributesWithRange &Attrs) { const char *SemiError = nullptr; StmtResult Res; @@ -202,7 +207,8 @@ Retry: } default: { - if ((getLangOpts().CPlusPlus || !OnlyStatement) && isDeclarationStatement()) { + if ((getLangOpts().CPlusPlus || Allowed == ACK_Any) && + isDeclarationStatement()) { SourceLocation DeclStart = Tok.getLocation(), DeclEnd; DeclGroupPtrTy Decl = ParseDeclaration(Declarator::BlockContext, DeclEnd, Attrs); @@ -346,7 +352,7 @@ Retry: case tok::annot_pragma_openmp: ProhibitAttributes(Attrs); - return ParseOpenMPDeclarativeOrExecutableDirective(!OnlyStatement); + return ParseOpenMPDeclarativeOrExecutableDirective(Allowed); case tok::annot_pragma_ms_pointers_to_members: ProhibitAttributes(Attrs); @@ -365,7 +371,11 @@ Retry: case tok::annot_pragma_loop_hint: ProhibitAttributes(Attrs); - return ParsePragmaLoopHint(Stmts, OnlyStatement, TrailingElseLoc, Attrs); + return ParsePragmaLoopHint(Stmts, Allowed, TrailingElseLoc, Attrs); + + case tok::annot_pragma_dump: + HandlePragmaDump(); + return StmtEmpty(); } // If we reached this code, the statement must end in a semicolon. @@ -583,7 +593,8 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributesWithRange &attrs) { // can't handle GNU attributes), so only call it in the one case where // GNU attributes are allowed. SubStmt = ParseStatementOrDeclarationAfterAttributes( - Stmts, /*OnlyStmts*/ true, nullptr, TempAttrs); + Stmts, /*Allowed=*/ACK_StatementsOpenMPNonStandalone, nullptr, + TempAttrs); if (!TempAttrs.empty() && !SubStmt.isInvalid()) SubStmt = Actions.ProcessStmtAttributes( SubStmt.get(), TempAttrs.getList(), TempAttrs.Range); @@ -722,7 +733,8 @@ StmtResult Parser::ParseCaseStatement(bool MissingCase, ExprResult Expr) { // continue parsing the sub-stmt. if (Case.isInvalid()) { if (TopLevelCase.isInvalid()) // No parsed case stmts. - return ParseStatement(); + return ParseStatement(/*TrailingElseLoc=*/nullptr, + /*AllowOpenMPStandalone=*/true); // Otherwise, just don't add it as a nested case. } else { // If this is the first case statement we parsed, it becomes TopLevelCase. @@ -742,7 +754,8 @@ StmtResult Parser::ParseCaseStatement(bool MissingCase, ExprResult Expr) { StmtResult SubStmt; if (Tok.isNot(tok::r_brace)) { - SubStmt = ParseStatement(); + SubStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, + /*AllowOpenMPStandalone=*/true); } else { // Nicely diagnose the common error "switch (X) { case 4: }", which is // not valid. If ColonLoc doesn't point to a valid text location, there was @@ -794,7 +807,8 @@ StmtResult Parser::ParseDefaultStatement() { StmtResult SubStmt; if (Tok.isNot(tok::r_brace)) { - SubStmt = ParseStatement(); + SubStmt = ParseStatement(/*TrailingElseLoc=*/nullptr, + /*AllowOpenMPStandalone=*/true); } else { // Diagnose the common error "switch (X) {... default: }", which is // not valid. @@ -893,6 +907,9 @@ void Parser::ParseCompoundStatementLeadingPragmas() { case tok::annot_pragma_ms_vtordisp: HandlePragmaMSVtorDisp(); break; + case tok::annot_pragma_dump: + HandlePragmaDump(); + break; default: checkForPragmas = false; break; @@ -965,7 +982,7 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { StmtResult R; if (Tok.isNot(tok::kw___extension__)) { - R = ParseStatementOrDeclaration(Stmts, false); + R = ParseStatementOrDeclaration(Stmts, ACK_Any); } else { // __extension__ can start declarations and it can also be a unary // operator for expressions. Consume multiple __extension__ markers here @@ -1861,7 +1878,8 @@ StmtResult Parser::ParseReturnStatement() { return Actions.ActOnReturnStmt(ReturnLoc, R.get(), getCurScope()); } -StmtResult Parser::ParsePragmaLoopHint(StmtVector &Stmts, bool OnlyStatement, +StmtResult Parser::ParsePragmaLoopHint(StmtVector &Stmts, + AllowedContsructsKind Allowed, SourceLocation *TrailingElseLoc, ParsedAttributesWithRange &Attrs) { // Create temporary attribute list. @@ -1884,7 +1902,7 @@ StmtResult Parser::ParsePragmaLoopHint(StmtVector &Stmts, bool OnlyStatement, MaybeParseCXX11Attributes(Attrs); StmtResult S = ParseStatementOrDeclarationAfterAttributes( - Stmts, OnlyStatement, TrailingElseLoc, Attrs); + Stmts, Allowed, TrailingElseLoc, Attrs); Attrs.takeAllFrom(TempAttrs); return S; @@ -2182,7 +2200,7 @@ void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) { // Condition is true, parse the statements. while (Tok.isNot(tok::r_brace)) { - StmtResult R = ParseStatementOrDeclaration(Stmts, false); + StmtResult R = ParseStatementOrDeclaration(Stmts, ACK_Any); if (R.isUsable()) Stmts.push_back(R.get()); } diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index b3eeb9d58ff4..ccefb3dd3f5d 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -668,6 +668,9 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, case tok::annot_pragma_ms_pragma: HandlePragmaMSPragma(); return DeclGroupPtrTy(); + case tok::annot_pragma_dump: + HandlePragmaDump(); + return DeclGroupPtrTy(); case tok::semi: // Either a C++11 empty-declaration or attribute-declaration. SingleDecl = Actions.ActOnEmptyDeclaration(getCurScope(), diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index d664d8704003..6f6c4ca5848f 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -270,6 +270,7 @@ bool Declarator::isDeclarationOfFunction() const { case DeclaratorChunk::Array: case DeclaratorChunk::BlockPointer: case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: return false; } llvm_unreachable("Invalid type chunk"); @@ -713,6 +714,22 @@ bool DeclSpec::SetTypeAltiVecVector(bool isAltiVecVector, SourceLocation Loc, return false; } +bool DeclSpec::SetTypePipe(bool isPipe, SourceLocation Loc, + const char *&PrevSpec, unsigned &DiagID, + const PrintingPolicy &Policy) { + + if (TypeSpecType != TST_unspecified) { + PrevSpec = DeclSpec::getSpecifierName((TST)TypeSpecType, Policy); + DiagID = diag::err_invalid_decl_spec_combination; + return true; + } + + if (isPipe) { + TypeSpecPipe = TSP_pipe; + } + return false; +} + bool DeclSpec::SetTypeAltiVecPixel(bool isAltiVecPixel, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, const PrintingPolicy &Policy) { diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index 07b058911c2d..ad1d7da4d070 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -2105,6 +2105,7 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle, && (SrcExpr.get()->getType()->isIntegerType() || SrcExpr.get()->getType()->isFloatingType())) { Kind = CK_VectorSplat; + SrcExpr = Self.prepareVectorSplat(DestType, SrcExpr.get()); return; } @@ -2339,6 +2340,7 @@ void CastOperation::CheckCStyleCast() { if (DestVecTy->getVectorKind() == VectorType::AltiVecVector && (SrcType->isIntegerType() || SrcType->isFloatingType())) { Kind = CK_VectorSplat; + SrcExpr = Self.prepareVectorSplat(DestType, SrcExpr.get()); } else if (Self.CheckVectorCast(OpRange, DestType, SrcType, Kind)) { SrcExpr = ExprError(); } diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index cbdcb5e48391..6c2834b750ae 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -6243,7 +6243,8 @@ static IntRange GetExprRange(ASTContext &C, Expr *E, unsigned MaxWidth) { IntRange OutputTypeRange = IntRange::forValueOfType(C, GetExprType(CE)); - bool isIntegerCast = (CE->getCastKind() == CK_IntegralCast); + bool isIntegerCast = CE->getCastKind() == CK_IntegralCast || + CE->getCastKind() == CK_BooleanToSignedIntegral; // Assume that non-integer casts can span the full range of the type. if (!isIntegerCast) @@ -7047,6 +7048,10 @@ static void DiagnoseNullConversion(Sema &S, Expr *E, QualType T, E->getExprLoc())) return; + // Don't warn on functions which have return type nullptr_t. + if (isa<CallExpr>(E)) + return; + // Check for NULL (GNUNull) or nullptr (CXX11_nullptr). const Expr::NullPointerConstantKind NullKind = E->isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull); @@ -7062,8 +7067,12 @@ static void DiagnoseNullConversion(Sema &S, Expr *E, QualType T, // __null is usually wrapped in a macro. Go up a macro if that is the case. if (NullKind == Expr::NPCK_GNUNull) { - if (Loc.isMacroID()) - Loc = S.SourceMgr.getImmediateExpansionRange(Loc).first; + if (Loc.isMacroID()) { + StringRef MacroName = + Lexer::getImmediateMacroName(Loc, S.SourceMgr, S.getLangOpts()); + if (MacroName == "NULL") + Loc = S.SourceMgr.getImmediateExpansionRange(Loc).first; + } } // Only warn if the null and context location are in the same macro expansion. @@ -7845,6 +7854,10 @@ void Sema::CheckBoolLikeConversion(Expr *E, SourceLocation CC) { void Sema::CheckForIntOverflow (Expr *E) { if (isa<BinaryOperator>(E->IgnoreParenCasts())) E->IgnoreParenCasts()->EvaluateForOverflow(Context); + else if (auto InitList = dyn_cast<InitListExpr>(E)) + for (Expr *E : InitList->inits()) + if (isa<BinaryOperator>(E->IgnoreParenCasts())) + E->IgnoreParenCasts()->EvaluateForOverflow(Context); } namespace { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index f27fb2b10712..f95d1068cc59 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3962,9 +3962,6 @@ static bool CheckAnonMemberRedeclaration(Sema &SemaRef, Sema::ForRedeclaration); if (!SemaRef.LookupName(R, S)) return false; - if (R.getAsSingle<TagDecl>()) - return false; - // Pick a representative declaration. NamedDecl *PrevDecl = R.getRepresentativeDecl()->getUnderlyingDecl(); assert(PrevDecl && "Expected a non-null Decl"); @@ -4675,11 +4672,13 @@ bool Sema::DiagnoseClassNameShadow(DeclContext *DC, DeclarationNameInfo NameInfo) { DeclarationName Name = NameInfo.getName(); - if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(DC)) - if (Record->getIdentifier() && Record->getDeclName() == Name) { - Diag(NameInfo.getLoc(), diag::err_member_name_of_class) << Name; - return true; - } + CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(DC); + while (Record && Record->isAnonymousStructOrUnion()) + Record = dyn_cast<CXXRecordDecl>(Record->getParent()); + if (Record && Record->getIdentifier() && Record->getDeclName() == Name) { + Diag(NameInfo.getLoc(), diag::err_member_name_of_class) << Name; + return true; + } return false; } @@ -8257,6 +8256,23 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, for (auto Param : NewFD->params()) checkIsValidOpenCLKernelParameter(*this, D, Param, ValidTypes); } + for (FunctionDecl::param_iterator PI = NewFD->param_begin(), + PE = NewFD->param_end(); PI != PE; ++PI) { + ParmVarDecl *Param = *PI; + QualType PT = Param->getType(); + + // OpenCL 2.0 pipe restrictions forbids pipe packet types to be non-value + // types. + if (getLangOpts().OpenCLVersion >= 200) { + if(const PipeType *PipeTy = PT->getAs<PipeType>()) { + QualType ElemTy = PipeTy->getElementType(); + if (ElemTy->isReferenceType() || ElemTy->isPointerType()) { + Diag(Param->getTypeSpecStartLoc(), diag::err_reference_pipe_type ); + D.setInvalidType(); + } + } + } + } MarkUnusedFileScopedDecl(NewFD); @@ -11799,6 +11815,28 @@ static bool isAcceptableTagRedeclContext(Sema &S, DeclContext *OldDC, return false; } +/// Find the DeclContext in which a tag is implicitly declared if we see an +/// elaborated type specifier in the specified context, and lookup finds +/// nothing. +static DeclContext *getTagInjectionContext(DeclContext *DC) { + while (!DC->isFileContext() && !DC->isFunctionOrMethod()) + DC = DC->getParent(); + return DC; +} + +/// Find the Scope in which a tag is implicitly declared if we see an +/// elaborated type specifier in the specified context, and lookup finds +/// nothing. +static Scope *getTagInjectionScope(Scope *S, const LangOptions &LangOpts) { + while (S->isClassScope() || + (LangOpts.CPlusPlus && + S->isFunctionPrototypeScope()) || + ((S->getFlags() & Scope::DeclScope) == 0) || + (S->getEntity() && S->getEntity()->isTransparentContext())) + S = S->getParent(); + return S; +} + /// \brief This is invoked when we see 'struct foo' or 'struct {'. In the /// former case, Name will be non-null. In the later case, Name will be null. /// TagSpec indicates what kind of tag this is. TUK indicates whether this is a @@ -12115,16 +12153,10 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // Find the context where we'll be declaring the tag. // FIXME: We would like to maintain the current DeclContext as the // lexical context, - while (!SearchDC->isFileContext() && !SearchDC->isFunctionOrMethod()) - SearchDC = SearchDC->getParent(); + SearchDC = getTagInjectionContext(SearchDC); // Find the scope where we'll be declaring the tag. - while (S->isClassScope() || - (getLangOpts().CPlusPlus && - S->isFunctionPrototypeScope()) || - ((S->getFlags() & Scope::DeclScope) == 0) || - (S->getEntity() && S->getEntity()->isTransparentContext())) - S = S->getParent(); + S = getTagInjectionScope(S, getLangOpts()); } else { assert(TUK == TUK_Friend); // C++ [namespace.memdef]p3: @@ -12284,7 +12316,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, } else if (TUK == TUK_Reference && (PrevTagDecl->getFriendObjectKind() == Decl::FOK_Undeclared || - getOwningModule(PrevDecl) != + PP.getModuleContainingLocation( + PrevDecl->getLocation()) != PP.getModuleContainingLocation(KWLoc)) && SS.isEmpty()) { // This declaration is a reference to an existing entity, but @@ -12294,14 +12327,12 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // the declaration would have meant the same thing if no prior // declaration were found, that is, if it was found in the same // scope where we would have injected a declaration. - DeclContext *InjectedDC = CurContext; - while (!InjectedDC->isFileContext() && - !InjectedDC->isFunctionOrMethod()) - InjectedDC = InjectedDC->getParent(); - if (!InjectedDC->getRedeclContext()->Equals( - PrevDecl->getDeclContext()->getRedeclContext())) + if (!getTagInjectionContext(CurContext)->getRedeclContext() + ->Equals(PrevDecl->getDeclContext()->getRedeclContext())) return PrevTagDecl; - // This is in the injected scope, create a new declaration. + // This is in the injected scope, create a new declaration in + // that scope. + S = getTagInjectionScope(S, getLangOpts()); } else { return PrevTagDecl; } @@ -12603,7 +12634,7 @@ CreateNewDecl: << Name; Invalid = true; } - } else { + } else if (!PrevDecl) { Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New); } DeclsInPrototypeScope.push_back(New); diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 5a0f0f84af7e..f94c822b90f5 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -348,6 +348,25 @@ static void handleSimpleAttribute(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } +template <typename AttrType> +static void handleSimpleAttributeWithExclusions(Sema &S, Decl *D, + const AttributeList &Attr) { + handleSimpleAttribute<AttrType>(S, D, Attr); +} + +/// \brief Applies the given attribute to the Decl so long as the Decl doesn't +/// already have one of the given incompatible attributes. +template <typename AttrType, typename IncompatibleAttrType, + typename... IncompatibleAttrTypes> +static void handleSimpleAttributeWithExclusions(Sema &S, Decl *D, + const AttributeList &Attr) { + if (checkAttrMutualExclusion<IncompatibleAttrType>(S, D, Attr.getRange(), + Attr.getName())) + return; + handleSimpleAttributeWithExclusions<AttrType, IncompatibleAttrTypes...>(S, D, + Attr); +} + /// \brief Check if the passed-in expression is of type int or bool. static bool isIntOrBool(Expr *Exp) { QualType QT = Exp->getType(); @@ -3588,6 +3607,12 @@ static void handleOptimizeNoneAttr(Sema &S, Decl *D, } static void handleGlobalAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (checkAttrMutualExclusion<CUDADeviceAttr>(S, D, Attr.getRange(), + Attr.getName()) || + checkAttrMutualExclusion<CUDAHostAttr>(S, D, Attr.getRange(), + Attr.getName())) { + return; + } FunctionDecl *FD = cast<FunctionDecl>(D); if (!FD->getReturnType()->isVoidType()) { SourceRange RTRange = FD->getReturnTypeSourceRange(); @@ -4558,14 +4583,6 @@ static void handleInterruptAttr(Sema &S, Decl *D, const AttributeList &Attr) { handleARMInterruptAttr(S, D, Attr); } -static void handleMips16Attribute(Sema &S, Decl *D, const AttributeList &Attr) { - if (checkAttrMutualExclusion<MipsInterruptAttr>(S, D, Attr.getRange(), - Attr.getName())) - return; - - handleSimpleAttribute<Mips16Attr>(S, D, Attr); -} - static void handleAMDGPUNumVGPRAttr(Sema &S, Decl *D, const AttributeList &Attr) { uint32_t NumRegs; @@ -4955,7 +4972,8 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, handleDLLAttr(S, D, Attr); break; case AttributeList::AT_Mips16: - handleMips16Attribute(S, D, Attr); + handleSimpleAttributeWithExclusions<Mips16Attr, MipsInterruptAttr>(S, D, + Attr); break; case AttributeList::AT_NoMips16: handleSimpleAttribute<NoMips16Attr>(S, D, Attr); @@ -5006,7 +5024,8 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, handleCommonAttr(S, D, Attr); break; case AttributeList::AT_CUDAConstant: - handleSimpleAttribute<CUDAConstantAttr>(S, D, Attr); + handleSimpleAttributeWithExclusions<CUDAConstantAttr, CUDASharedAttr>(S, D, + Attr); break; case AttributeList::AT_PassObjectSize: handlePassObjectSizeAttr(S, D, Attr); @@ -5051,10 +5070,12 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, handleGlobalAttr(S, D, Attr); break; case AttributeList::AT_CUDADevice: - handleSimpleAttribute<CUDADeviceAttr>(S, D, Attr); + handleSimpleAttributeWithExclusions<CUDADeviceAttr, CUDAGlobalAttr>(S, D, + Attr); break; case AttributeList::AT_CUDAHost: - handleSimpleAttribute<CUDAHostAttr>(S, D, Attr); + handleSimpleAttributeWithExclusions<CUDAHostAttr, CUDAGlobalAttr>(S, D, + Attr); break; case AttributeList::AT_GNUInline: handleGNUInlineAttr(S, D, Attr); @@ -5114,7 +5135,8 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, handleSimpleAttribute<NoThrowAttr>(S, D, Attr); break; case AttributeList::AT_CUDAShared: - handleSimpleAttribute<CUDASharedAttr>(S, D, Attr); + handleSimpleAttributeWithExclusions<CUDASharedAttr, CUDAConstantAttr>(S, D, + Attr); break; case AttributeList::AT_VecReturn: handleVecReturnAttr(S, D, Attr); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 02091a7bd530..11f232934e5a 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -7000,6 +7000,7 @@ void Sema::CheckConversionDeclarator(Declarator &D, QualType &R, case DeclaratorChunk::BlockPointer: case DeclaratorChunk::Reference: case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: extendLeft(Before, Chunk.getSourceRange()); break; @@ -7796,6 +7797,10 @@ bool Sema::CheckUsingShadowDecl(UsingDecl *Using, NamedDecl *Orig, if (UsingShadowDecl *Shadow = dyn_cast<UsingShadowDecl>(*I)) PrevShadow = Shadow; FoundEquivalentDecl = true; + } else if (isEquivalentInternalLinkageDeclaration(D, Target)) { + // We don't conflict with an existing using shadow decl of an equivalent + // declaration, but we're not a redeclaration of it. + FoundEquivalentDecl = true; } if (isVisible(D)) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 5d0c6057f54f..3e89af625d1a 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3084,6 +3084,8 @@ ExprResult Sema::ActOnCharacterConstant(const Token &Tok, Scope *UDLScope) { Kind = CharacterLiteral::UTF16; else if (Literal.isUTF32()) Kind = CharacterLiteral::UTF32; + else if (Literal.isUTF8()) + Kind = CharacterLiteral::UTF8; Expr *Lit = new (Context) CharacterLiteral(Literal.getValue(), Kind, Ty, Tok.getLocation()); @@ -4313,10 +4315,16 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, if (Result.isInvalid()) return ExprError(); - Expr *Arg = Result.getAs<Expr>(); - CheckCompletedExpr(Arg, Param->getOuterLocStart()); - // Build the default argument expression. - return CXXDefaultArgExpr::Create(Context, CallLoc, Param, Arg); + Result = ActOnFinishFullExpr(Result.getAs<Expr>(), + Param->getOuterLocStart()); + if (Result.isInvalid()) + return ExprError(); + + // Remember the instantiated default argument. + Param->setDefaultArg(Result.getAs<Expr>()); + if (ASTMutationListener *L = getASTMutationListener()) { + L->DefaultArgumentInstantiated(Param); + } } // If the default expression creates temporaries, we need to @@ -4929,7 +4937,9 @@ Sema::ActOnCallExpr(Scope *S, Expr *Fn, SourceLocation LParenLoc, OverloadExpr *ovl = find.Expression; if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(ovl)) return BuildOverloadedCallExpr(S, Fn, ULE, LParenLoc, ArgExprs, - RParenLoc, ExecConfig); + RParenLoc, ExecConfig, + /*AllowTypoCorrection=*/true, + find.IsAddressOfOperand); return BuildCallToMemberFunction(S, Fn, LParenLoc, ArgExprs, RParenLoc); } } @@ -4943,10 +4953,14 @@ Sema::ActOnCallExpr(Scope *S, Expr *Fn, SourceLocation LParenLoc, Expr *NakedFn = Fn->IgnoreParens(); + bool CallingNDeclIndirectly = false; NamedDecl *NDecl = nullptr; - if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(NakedFn)) - if (UnOp->getOpcode() == UO_AddrOf) + if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(NakedFn)) { + if (UnOp->getOpcode() == UO_AddrOf) { + CallingNDeclIndirectly = true; NakedFn = UnOp->getSubExpr()->IgnoreParens(); + } + } if (isa<DeclRefExpr>(NakedFn)) { NDecl = cast<DeclRefExpr>(NakedFn)->getDecl(); @@ -4968,6 +4982,11 @@ Sema::ActOnCallExpr(Scope *S, Expr *Fn, SourceLocation LParenLoc, NDecl = cast<MemberExpr>(NakedFn)->getMemberDecl(); if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(NDecl)) { + if (CallingNDeclIndirectly && + !checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true, + Fn->getLocStart())) + return ExprError(); + if (FD->hasAttr<EnableIfAttr>()) { if (const EnableIfAttr *Attr = CheckEnableIf(FD, ArgExprs, true)) { Diag(Fn->getLocStart(), @@ -5583,6 +5602,39 @@ bool Sema::CheckVectorCast(SourceRange R, QualType VectorTy, QualType Ty, return false; } +ExprResult Sema::prepareVectorSplat(QualType VectorTy, Expr *SplattedExpr) { + QualType DestElemTy = VectorTy->castAs<VectorType>()->getElementType(); + + if (DestElemTy == SplattedExpr->getType()) + return SplattedExpr; + + assert(DestElemTy->isFloatingType() || + DestElemTy->isIntegralOrEnumerationType()); + + CastKind CK; + if (VectorTy->isExtVectorType() && SplattedExpr->getType()->isBooleanType()) { + // OpenCL requires that we convert `true` boolean expressions to -1, but + // only when splatting vectors. + if (DestElemTy->isFloatingType()) { + // To avoid having to have a CK_BooleanToSignedFloating cast kind, we cast + // in two steps: boolean to signed integral, then to floating. + ExprResult CastExprRes = ImpCastExprToType(SplattedExpr, Context.IntTy, + CK_BooleanToSignedIntegral); + SplattedExpr = CastExprRes.get(); + CK = CK_IntegralToFloating; + } else { + CK = CK_BooleanToSignedIntegral; + } + } else { + ExprResult CastExprRes = SplattedExpr; + CK = PrepareScalarCast(CastExprRes, DestElemTy); + if (CastExprRes.isInvalid()) + return ExprError(); + SplattedExpr = CastExprRes.get(); + } + return ImpCastExprToType(SplattedExpr, DestElemTy, CK); +} + ExprResult Sema::CheckExtVectorCast(SourceRange R, QualType DestTy, Expr *CastExpr, CastKind &Kind) { assert(DestTy->isExtVectorType() && "Not an extended vector type!"); @@ -5613,15 +5665,8 @@ ExprResult Sema::CheckExtVectorCast(SourceRange R, QualType DestTy, diag::err_invalid_conversion_between_vector_and_scalar) << DestTy << SrcTy << R; - QualType DestElemTy = DestTy->getAs<ExtVectorType>()->getElementType(); - ExprResult CastExprRes = CastExpr; - CastKind CK = PrepareScalarCast(CastExprRes, DestElemTy); - if (CastExprRes.isInvalid()) - return ExprError(); - CastExpr = ImpCastExprToType(CastExprRes.get(), DestElemTy, CK).get(); - Kind = CK_VectorSplat; - return CastExpr; + return prepareVectorSplat(DestTy, CastExpr); } ExprResult @@ -6960,13 +7005,9 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, if (RHSType->isExtVectorType()) return Incompatible; if (RHSType->isArithmeticType()) { - // CK_VectorSplat does T -> vector T, so first cast to the - // element type. - QualType elType = cast<ExtVectorType>(LHSType)->getElementType(); - if (elType != RHSType && ConvertRHS) { - Kind = PrepareScalarCast(RHS, elType); - RHS = ImpCastExprToType(RHS.get(), elType, Kind); - } + // CK_VectorSplat does T -> vector T, so first cast to the element type. + if (ConvertRHS) + RHS = prepareVectorSplat(LHSType, RHS.get()); Kind = CK_VectorSplat; return Compatible; } @@ -8184,7 +8225,7 @@ static QualType checkOpenCLVectorShift(Sema &S, if (RHS.isInvalid()) return QualType(); QualType LHSType = LHS.get()->getType(); - const VectorType *LHSVecTy = LHSType->getAs<VectorType>(); + const VectorType *LHSVecTy = LHSType->castAs<VectorType>(); QualType LHSEleType = LHSVecTy->getElementType(); // Note that RHS might not be a vector. @@ -13121,6 +13162,7 @@ bool Sema::tryCaptureVariable( case Type::ObjCObject: case Type::ObjCInterface: case Type::ObjCObjectPointer: + case Type::Pipe: llvm_unreachable("type class is never variably-modified!"); case Type::Adjusted: QTy = cast<AdjustedType>(Ty)->getOriginalType(); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 2ad595f3a814..38fbea18d790 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -3353,20 +3353,13 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, VK_RValue, /*BasePath=*/nullptr, CCK).get(); break; - case ICK_Vector_Splat: + case ICK_Vector_Splat: { // Vector splat from any arithmetic type to a vector. - // Cast to the element type. - { - QualType elType = ToType->getAs<ExtVectorType>()->getElementType(); - if (elType != From->getType()) { - ExprResult E = From; - From = ImpCastExprToType(From, elType, - PrepareScalarCast(E, elType)).get(); - } - From = ImpCastExprToType(From, ToType, CK_VectorSplat, - VK_RValue, /*BasePath=*/nullptr, CCK).get(); - } + Expr *Elem = prepareVectorSplat(ToType, From).get(); + From = ImpCastExprToType(Elem, ToType, CK_VectorSplat, VK_RValue, + /*BasePath=*/nullptr, CCK).get(); break; + } case ICK_Complex_Real: // Case 1. x -> _Complex y diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 57a08b94f5e8..1d86ca35412e 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -319,6 +319,7 @@ ExprResult Sema::BuildObjCNumericLiteral(SourceLocation AtLoc, Expr *Number) { // to use to determine the Objective-c literal kind. switch (Char->getKind()) { case CharacterLiteral::Ascii: + case CharacterLiteral::UTF8: NumberType = Context.CharTy; break; @@ -577,6 +578,7 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { // to use to determine the Objective-c literal kind. switch (Char->getKind()) { case CharacterLiteral::Ascii: + case CharacterLiteral::UTF8: ValueType = Context.CharTy; break; diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 481ae6cd55b1..45dc2e33da93 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -650,6 +650,13 @@ void LookupResult::print(raw_ostream &Out) { } } +LLVM_DUMP_METHOD void LookupResult::dump() { + llvm::errs() << "lookup results for " << getLookupName().getAsString() + << ":\n"; + for (NamedDecl *D : *this) + D->dump(); +} + /// \brief Lookup a builtin function, when name lookup would otherwise /// fail. static bool LookupBuiltin(Sema &S, LookupResult &R) { @@ -2616,6 +2623,9 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) { case Type::Atomic: T = cast<AtomicType>(T)->getValueType().getTypePtr(); continue; + case Type::Pipe: + T = cast<PipeType>(T)->getElementType().getTypePtr(); + continue; } if (Queue.empty()) @@ -4988,3 +4998,12 @@ const Sema::TypoExprState &Sema::getTypoExprState(TypoExpr *TE) const { void Sema::clearDelayedTypo(TypoExpr *TE) { DelayedTypos.erase(TE); } + +void Sema::ActOnPragmaDump(Scope *S, SourceLocation IILoc, IdentifierInfo *II) { + DeclarationNameInfo Name(II, IILoc); + LookupResult R(*this, Name, LookupAnyName, Sema::NotForRedeclaration); + R.suppressDiagnostics(); + R.setHideTags(false); + LookupName(R, S); + R.dump(); +} diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index e0c10e4479e2..663da0c0e804 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -258,6 +258,7 @@ static const Expr *IgnoreNarrowingConversion(const Expr *Converted) { case CK_IntegralCast: case CK_IntegralToBoolean: case CK_IntegralToFloating: + case CK_BooleanToSignedIntegral: case CK_FloatingToIntegral: case CK_FloatingToBoolean: case CK_FloatingCast: @@ -9643,6 +9644,13 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, case ovl_fail_enable_if: return DiagnoseFailedEnableIfAttr(S, Cand); + + case ovl_fail_addr_not_available: { + bool Available = checkAddressOfCandidateIsAvailable(S, Cand->Function); + (void)Available; + assert(!Available); + break; + } } } @@ -11245,6 +11253,17 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, return ExprError(); } +static void markUnaddressableCandidatesUnviable(Sema &S, + OverloadCandidateSet &CS) { + for (auto I = CS.begin(), E = CS.end(); I != E; ++I) { + if (I->Viable && + !S.checkAddressOfFunctionIsAvailable(I->Function, /*Complain=*/false)) { + I->Viable = false; + I->FailureKind = ovl_fail_addr_not_available; + } + } +} + /// BuildOverloadedCallExpr - Given the call expression that calls Fn /// (which eventually refers to the declaration Func) and the call /// arguments Args/NumArgs, attempt to resolve the function call down @@ -11257,7 +11276,8 @@ ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, MultiExprArg Args, SourceLocation RParenLoc, Expr *ExecConfig, - bool AllowTypoCorrection) { + bool AllowTypoCorrection, + bool CalleesAddressIsTaken) { OverloadCandidateSet CandidateSet(Fn->getExprLoc(), OverloadCandidateSet::CSK_Normal); ExprResult result; @@ -11266,6 +11286,11 @@ ExprResult Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, &result)) return result; + // If the user handed us something like `(&Foo)(Bar)`, we need to ensure that + // functions that aren't addressible are considered unviable. + if (CalleesAddressIsTaken) + markUnaddressableCandidatesUnviable(*this, CandidateSet); + OverloadCandidateSet::iterator Best; OverloadingResult OverloadResult = CandidateSet.BestViableFunction(*this, Fn->getLocStart(), Best); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 6cc85883345d..57156078c80b 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -4184,6 +4184,10 @@ bool UnnamedLocalNoLinkageFinder::VisitAtomicType(const AtomicType* T) { return Visit(T->getValueType()); } +bool UnnamedLocalNoLinkageFinder::VisitPipeType(const PipeType* T) { + return false; +} + bool UnnamedLocalNoLinkageFinder::VisitTagDecl(const TagDecl *Tag) { if (Tag->getDeclContext()->isFunctionOrMethod()) { S.Diag(SR.getBegin(), @@ -5503,6 +5507,8 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, Expr *E; if (T->isAnyCharacterType()) { + // This does not need to handle u8 character literals because those are + // of type char, and so can also be covered by an ASCII character literal. CharacterLiteral::CharacterKind Kind; if (T->isWideCharType()) Kind = CharacterLiteral::Wide; diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index cd54920b08cf..71faafc6bc12 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -1652,6 +1652,7 @@ DeduceTemplateArgumentsByTypeMatch(Sema &S, case Type::Auto: case Type::DependentTemplateSpecialization: case Type::PackExpansion: + case Type::Pipe: // No template argument deduction for these types return Sema::TDK_Success; } @@ -4964,6 +4965,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T, case Type::ObjCObject: case Type::ObjCObjectPointer: case Type::UnresolvedUsing: + case Type::Pipe: #define TYPE(Class, Base) #define ABSTRACT_TYPE(Class, Base) #define DEPENDENT_TYPE(Class, Base) diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index 61052f06c834..cb67d71f9e59 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -750,6 +750,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) { case DeclaratorChunk::Pointer: case DeclaratorChunk::Reference: case DeclaratorChunk::Paren: + case DeclaratorChunk::Pipe: case DeclaratorChunk::BlockPointer: // These declarator chunks cannot contain any parameter packs. break; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index c70568c23b57..f6ad132cde83 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -335,6 +335,7 @@ static DeclaratorChunk *maybeMovePastReturnType(Declarator &declarator, case DeclaratorChunk::Array: case DeclaratorChunk::Reference: case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: return result; // If we do find a function declarator, scan inwards from that, @@ -347,6 +348,7 @@ static DeclaratorChunk *maybeMovePastReturnType(Declarator &declarator, case DeclaratorChunk::Array: case DeclaratorChunk::Function: case DeclaratorChunk::Reference: + case DeclaratorChunk::Pipe: continue; case DeclaratorChunk::MemberPointer: @@ -427,6 +429,7 @@ static void distributeObjCPointerTypeAttr(TypeProcessingState &state, // Don't walk through these. case DeclaratorChunk::Reference: case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: goto error; } } @@ -459,6 +462,7 @@ distributeObjCPointerTypeAttrFromDeclarator(TypeProcessingState &state, case DeclaratorChunk::MemberPointer: case DeclaratorChunk::Paren: case DeclaratorChunk::Array: + case DeclaratorChunk::Pipe: continue; case DeclaratorChunk::Function: @@ -520,6 +524,7 @@ static void distributeFunctionTypeAttr(TypeProcessingState &state, case DeclaratorChunk::Array: case DeclaratorChunk::Reference: case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: continue; } } @@ -1272,6 +1277,10 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // value being declared, poison it as invalid so we don't get chains of // errors. declarator.setInvalidType(true); + } else if (S.getLangOpts().OpenCLVersion >= 200 && DS.isTypeSpecPipe()){ + S.Diag(DeclLoc, diag::err_missing_actual_pipe_type) + << DS.getSourceRange(); + declarator.setInvalidType(true); } else { S.Diag(DeclLoc, diag::ext_missing_type_specifier) << DS.getSourceRange(); @@ -1564,7 +1573,9 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // Apply any type attributes from the decl spec. This may cause the // list of type attributes to be temporarily saved while the type // attributes are pushed around. - processTypeAttrs(state, Result, TAL_DeclSpec, DS.getAttributes().getList()); + // pipe attributes will be handled later ( at GetFullTypeForDeclarator ) + if (!DS.isTypeSpecPipe()) + processTypeAttrs(state, Result, TAL_DeclSpec, DS.getAttributes().getList()); // Apply const/volatile/restrict qualifiers to T. if (unsigned TypeQuals = DS.getTypeQualifiers()) { @@ -1924,6 +1935,21 @@ QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue, return Context.getRValueReferenceType(T); } +/// \brief Build a Pipe type. +/// +/// \param T The type to which we'll be building a Pipe. +/// +/// \param Loc We do not use it for now. +/// +/// \returns A suitable pipe type, if there are no errors. Otherwise, returns a +/// NULL type. +QualType Sema::BuildPipeType(QualType T, SourceLocation Loc) { + assert(!T->isObjCObjectType() && "Should build ObjCObjectPointerType"); + + // Build the pipe type. + return Context.getPipeType(T); +} + /// Check whether the specified array size makes the array type a VLA. If so, /// return true, if not, return the size of the array in SizeVal. static bool isArraySizeVLA(Sema &S, Expr *ArraySize, llvm::APSInt &SizeVal) { @@ -2393,6 +2419,7 @@ static void inferARCWriteback(TypeProcessingState &state, case DeclaratorChunk::Array: // suppress if written (id[])? case DeclaratorChunk::Function: case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: return; } } @@ -2532,6 +2559,7 @@ static void diagnoseRedundantReturnTypeQualifiers(Sema &S, QualType RetTy, case DeclaratorChunk::Reference: case DeclaratorChunk::Array: case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: // FIXME: We can't currently provide an accurate source location and a // fix-it hint for these. unsigned AtomicQual = RetTy->isAtomicType() ? DeclSpec::TQ_atomic : 0; @@ -3057,6 +3085,7 @@ static PointerDeclaratorKind classifyPointerDeclarator(Sema &S, switch (chunk.Kind) { case DeclaratorChunk::Array: case DeclaratorChunk::Function: + case DeclaratorChunk::Pipe: break; case DeclaratorChunk::BlockPointer: @@ -3305,6 +3334,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case DeclaratorChunk::Array: DiagKind = 2; break; + case DeclaratorChunk::Pipe: + break; } S.Diag(DeclChunk.Loc, DiagId) << DiagKind; @@ -3370,6 +3401,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, switch (chunk.Kind) { case DeclaratorChunk::Array: case DeclaratorChunk::Function: + case DeclaratorChunk::Pipe: break; case DeclaratorChunk::BlockPointer: @@ -3689,6 +3721,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, break; case DeclaratorChunk::Function: case DeclaratorChunk::BlockPointer: + case DeclaratorChunk::Pipe: // These are invalid anyway, so just ignore. break; } @@ -4038,7 +4071,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, break; } - case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::MemberPointer: { // The scope spec must refer to a class, or be dependent. CXXScopeSpec &SS = DeclType.Mem.Scope(); QualType ClsType; @@ -4098,6 +4131,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, break; } + case DeclaratorChunk::Pipe: { + T = S.BuildPipeType(T, DeclType.Loc ); + break; + } + } + if (T.isNull()) { D.setInvalidType(true); T = Context.IntTy; @@ -4392,6 +4431,7 @@ static void transferARCOwnership(TypeProcessingState &state, case DeclaratorChunk::Function: case DeclaratorChunk::MemberPointer: + case DeclaratorChunk::Pipe: return; } } @@ -4682,6 +4722,14 @@ namespace { } } + void VisitPipeTypeLoc(PipeTypeLoc TL) { + TL.setKWLoc(DS.getTypeSpecTypeLoc()); + + TypeSourceInfo *TInfo = 0; + Sema::GetTypeFromParser(DS.getRepAsType(), &TInfo); + TL.getValueLoc().initializeFullCopy(TInfo->getTypeLoc()); + } + void VisitTypeLoc(TypeLoc TL) { // FIXME: add other typespec types and change this to an assert. TL.initialize(Context, DS.getTypeSpecTypeLoc()); @@ -4802,6 +4850,10 @@ namespace { TL.setLParenLoc(Chunk.Loc); TL.setRParenLoc(Chunk.EndLoc); } + void VisitPipeTypeLoc(PipeTypeLoc TL) { + assert(Chunk.Kind == DeclaratorChunk::Pipe); + TL.setKWLoc(Chunk.Loc); + } void VisitTypeLoc(TypeLoc TL) { llvm_unreachable("unsupported TypeLoc kind in declarator!"); @@ -4815,6 +4867,7 @@ static void fillAtomicQualLoc(AtomicTypeLoc ATL, const DeclaratorChunk &Chunk) { case DeclaratorChunk::Function: case DeclaratorChunk::Array: case DeclaratorChunk::Paren: + case DeclaratorChunk::Pipe: llvm_unreachable("cannot be _Atomic qualified"); case DeclaratorChunk::Pointer: @@ -5738,6 +5791,7 @@ static bool distributeNullabilityTypeAttr(TypeProcessingState &state, // Don't walk through these. case DeclaratorChunk::Reference: + case DeclaratorChunk::Pipe: return false; } } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index e0a9653eb93b..935304fe4076 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1046,6 +1046,9 @@ public: /// Subclasses may override this routine to provide different behavior. QualType RebuildAtomicType(QualType ValueType, SourceLocation KWLoc); + /// \brief Build a new pipe type given its value type. + QualType RebuildPipeType(QualType ValueType, SourceLocation KWLoc); + /// \brief Build a new template name given a nested name specifier, a flag /// indicating whether the "template" keyword was provided, and the template /// that the template name refers to. @@ -3580,7 +3583,7 @@ void TreeTransform<Derived>::InventTemplateArgumentLoc( case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: { NestedNameSpecifierLocBuilder Builder; - TemplateName Template = Arg.getAsTemplate(); + TemplateName Template = Arg.getAsTemplateOrTemplatePattern(); if (DependentTemplateName *DTN = Template.getAsDependentTemplateName()) Builder.MakeTrivial(SemaRef.Context, DTN->getQualifier(), Loc); else if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName()) @@ -5324,6 +5327,26 @@ QualType TreeTransform<Derived>::TransformAtomicType(TypeLocBuilder &TLB, return Result; } +template <typename Derived> +QualType TreeTransform<Derived>::TransformPipeType(TypeLocBuilder &TLB, + PipeTypeLoc TL) { + QualType ValueType = getDerived().TransformType(TLB, TL.getValueLoc()); + if (ValueType.isNull()) + return QualType(); + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || ValueType != TL.getValueLoc().getType()) { + Result = getDerived().RebuildPipeType(ValueType, TL.getKWLoc()); + if (Result.isNull()) + return QualType(); + } + + PipeTypeLoc NewTL = TLB.push<PipeTypeLoc>(Result); + NewTL.setKWLoc(TL.getKWLoc()); + + return Result; +} + /// \brief Simple iterator that traverses the template arguments in a /// container that provides a \c getArgLoc() member function. /// @@ -6128,7 +6151,7 @@ TreeTransform<Derived>::TransformIfStmt(IfStmt *S) { } } - Sema::FullExprArg FullCond(getSema().MakeFullExpr(Cond.get())); + Sema::FullExprArg FullCond(getSema().MakeFullExpr(Cond.get(), S->getIfLoc())); if (!S->getConditionVariable() && S->getCond() && !FullCond.get()) return StmtError(); @@ -6223,7 +6246,8 @@ TreeTransform<Derived>::TransformWhileStmt(WhileStmt *S) { } } - Sema::FullExprArg FullCond(getSema().MakeFullExpr(Cond.get())); + Sema::FullExprArg FullCond( + getSema().MakeFullExpr(Cond.get(), S->getWhileLoc())); if (!S->getConditionVariable() && S->getCond() && !FullCond.get()) return StmtError(); @@ -6307,7 +6331,8 @@ TreeTransform<Derived>::TransformForStmt(ForStmt *S) { } } - Sema::FullExprArg FullCond(getSema().MakeFullExpr(Cond.get())); + Sema::FullExprArg FullCond( + getSema().MakeFullExpr(Cond.get(), S->getForLoc())); if (!S->getConditionVariable() && S->getCond() && !FullCond.get()) return StmtError(); @@ -11348,6 +11373,12 @@ QualType TreeTransform<Derived>::RebuildAtomicType(QualType ValueType, } template<typename Derived> +QualType TreeTransform<Derived>::RebuildPipeType(QualType ValueType, + SourceLocation KWLoc) { + return SemaRef.BuildPipeType(ValueType, KWLoc); +} + +template<typename Derived> TemplateName TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS, bool TemplateKW, diff --git a/lib/Serialization/ASTCommon.h b/lib/Serialization/ASTCommon.h index e59bc891f9b9..64f583c98728 100644 --- a/lib/Serialization/ASTCommon.h +++ b/lib/Serialization/ASTCommon.h @@ -29,6 +29,7 @@ enum DeclUpdateKind { UPD_CXX_ADDED_FUNCTION_DEFINITION, UPD_CXX_INSTANTIATED_STATIC_DATA_MEMBER, UPD_CXX_INSTANTIATED_CLASS_DEFINITION, + UPD_CXX_INSTANTIATED_DEFAULT_ARGUMENT, UPD_CXX_RESOLVED_DTOR_DELETE, UPD_CXX_RESOLVED_EXCEPTION_SPEC, UPD_CXX_DEDUCED_RETURN_TYPE, diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index a279475eeafb..833ff57e4d0b 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -5640,6 +5640,17 @@ QualType ASTReader::readTypeRecord(unsigned Index) { QualType ValueType = readType(*Loc.F, Record, Idx); return Context.getAtomicType(ValueType); } + + case TYPE_PIPE: { + if (Record.size() != 1) { + Error("Incorrect encoding of pipe type"); + return QualType(); + } + + // Reading the pipe element type. + QualType ElementType = readType(*Loc.F, Record, Idx); + return Context.getPipeType(ElementType); + } } llvm_unreachable("Invalid TypeCode!"); } @@ -5911,6 +5922,9 @@ void TypeLocReader::VisitAtomicTypeLoc(AtomicTypeLoc TL) { TL.setLParenLoc(ReadSourceLocation(Record, Idx)); TL.setRParenLoc(ReadSourceLocation(Record, Idx)); } +void TypeLocReader::VisitPipeTypeLoc(PipeTypeLoc TL) { + TL.setKWLoc(ReadSourceLocation(Record, Idx)); +} TypeSourceInfo *ASTReader::GetTypeSourceInfo(ModuleFile &F, const RecordData &Record, diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 8fb110e4551d..5bf95f878d49 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -3626,6 +3626,21 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile, Reader.ReadSourceLocation(ModuleFile, Record, Idx)); break; + case UPD_CXX_INSTANTIATED_DEFAULT_ARGUMENT: { + auto Param = cast<ParmVarDecl>(D); + + // We have to read the default argument regardless of whether we use it + // so that hypothetical further update records aren't messed up. + // TODO: Add a function to skip over the next expr record. + auto DefaultArg = Reader.ReadExpr(F); + + // Only apply the update if the parameter still has an uninstantiated + // default argument. + if (Param->hasUninstantiatedDefaultArg()) + Param->setDefaultArg(DefaultArg); + break; + } + case UPD_CXX_ADDED_FUNCTION_DEFINITION: { FunctionDecl *FD = cast<FunctionDecl>(D); if (Reader.PendingBodies[FD]) { diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index bc678aff7861..ad81ac844209 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -1364,10 +1364,7 @@ void ASTStmtReader::VisitCXXThrowExpr(CXXThrowExpr *E) { void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { VisitExpr(E); - - assert((bool)Record[Idx] == E->Param.getInt() && "We messed up at creation ?"); - ++Idx; // HasOtherExprStored and SubExpr was handled during creation. - E->Param.setPointer(ReadDeclAs<ParmVarDecl>(Record, Idx)); + E->Param = ReadDeclAs<ParmVarDecl>(Record, Idx); E->Loc = ReadSourceLocation(Record, Idx); } @@ -3205,16 +3202,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { case EXPR_CXX_THROW: S = new (Context) CXXThrowExpr(Empty); break; - case EXPR_CXX_DEFAULT_ARG: { - bool HasOtherExprStored = Record[ASTStmtReader::NumExprFields]; - if (HasOtherExprStored) { - Expr *SubExpr = ReadSubExpr(); - S = CXXDefaultArgExpr::Create(Context, SourceLocation(), nullptr, - SubExpr); - } else - S = new (Context) CXXDefaultArgExpr(Empty); + case EXPR_CXX_DEFAULT_ARG: + S = new (Context) CXXDefaultArgExpr(Empty); break; - } case EXPR_CXX_DEFAULT_INIT: S = new (Context) CXXDefaultInitExpr(Empty); break; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 0f50d7a42eab..ec04cd6c1fa9 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -446,6 +446,12 @@ ASTTypeWriter::VisitAtomicType(const AtomicType *T) { Code = TYPE_ATOMIC; } +void +ASTTypeWriter::VisitPipeType(const PipeType *T) { + Writer.AddTypeRef(T->getElementType(), Record); + Code = TYPE_PIPE; +} + namespace { class TypeLocWriter : public TypeLocVisitor<TypeLocWriter> { @@ -672,6 +678,9 @@ void TypeLocWriter::VisitAtomicTypeLoc(AtomicTypeLoc TL) { Writer.AddSourceLocation(TL.getLParenLoc(), Record); Writer.AddSourceLocation(TL.getRParenLoc(), Record); } +void TypeLocWriter::VisitPipeTypeLoc(PipeTypeLoc TL) { + Writer.AddSourceLocation(TL.getKWLoc(), Record); +} void ASTWriter::WriteTypeAbbrevs() { using namespace llvm; @@ -4611,6 +4620,11 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) { AddSourceLocation(Update.getLoc(), Record); break; + case UPD_CXX_INSTANTIATED_DEFAULT_ARGUMENT: + AddStmt(const_cast<Expr*>( + cast<ParmVarDecl>(Update.getDecl())->getDefaultArg())); + break; + case UPD_CXX_INSTANTIATED_CLASS_DEFINITION: { auto *RD = cast<CXXRecordDecl>(D); UpdatedDeclContexts.insert(RD->getPrimaryContext()); @@ -5779,6 +5793,15 @@ void ASTWriter::StaticDataMemberInstantiated(const VarDecl *D) { D->getMemberSpecializationInfo()->getPointOfInstantiation())); } +void ASTWriter::DefaultArgumentInstantiated(const ParmVarDecl *D) { + assert(!WritingAST && "Already writing the AST!"); + if (!D->isFromASTFile()) + return; + + DeclUpdates[D].push_back( + DeclUpdate(UPD_CXX_INSTANTIATED_DEFAULT_ARGUMENT, D)); +} + void ASTWriter::AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, const ObjCInterfaceDecl *IFD) { assert(!WritingAST && "Already writing the AST!"); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 20ca6d6fd512..54bba282ab8d 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -2033,7 +2033,7 @@ void ASTWriter::WriteDeclAbbrevs() { //Character Literal Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // getValue Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Location - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // getKind + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // getKind CharacterLiteralAbbrev = Stream.EmitAbbrev(Abv); // Abbreviation for EXPR_IMPLICIT_CAST diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index e52ed052d3bc..000a2185f5f0 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -1336,15 +1336,8 @@ void ASTStmtWriter::VisitCXXThrowExpr(CXXThrowExpr *E) { void ASTStmtWriter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { VisitExpr(E); - - bool HasOtherExprStored = E->Param.getInt(); - // Store these first, the reader reads them before creation. - Record.push_back(HasOtherExprStored); - if (HasOtherExprStored) - Writer.AddStmt(E->getExpr()); Writer.AddDeclRef(E->getParam(), Record); Writer.AddSourceLocation(E->getUsedLocation(), Record); - Code = serialization::EXPR_CXX_DEFAULT_ARG; } diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 5d78d9b02e6b..17537445d66c 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -948,15 +948,15 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR); switch (MR->getKind()) { - case MemRegion::FunctionTextRegionKind: { - const NamedDecl *FD = cast<FunctionTextRegion>(MR)->getDecl(); + case MemRegion::FunctionCodeRegionKind: { + const NamedDecl *FD = cast<FunctionCodeRegion>(MR)->getDecl(); if (FD) os << "the address of the function '" << *FD << '\''; else os << "the address of a function"; return true; } - case MemRegion::BlockTextRegionKind: + case MemRegion::BlockCodeRegionKind: os << "block text"; return true; case MemRegion::BlockDataRegionKind: diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index ce2c19409dc1..fee030feb6d2 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -1513,15 +1513,15 @@ bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { bool MallocChecker::SummarizeRegion(raw_ostream &os, const MemRegion *MR) { switch (MR->getKind()) { - case MemRegion::FunctionTextRegionKind: { - const NamedDecl *FD = cast<FunctionTextRegion>(MR)->getDecl(); + case MemRegion::FunctionCodeRegionKind: { + const NamedDecl *FD = cast<FunctionCodeRegion>(MR)->getDecl(); if (FD) os << "the address of the function '" << *FD << '\''; else os << "the address of a function"; return true; } - case MemRegion::BlockTextRegionKind: + case MemRegion::BlockCodeRegionKind: os << "block text"; return true; case MemRegion::BlockDataRegionKind: diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index a5b58710b215..175225ba0de2 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -316,7 +316,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_ArrayToPointerDecay: case CK_BitCast: case CK_AddressSpaceConversion: - case CK_IntegralCast: + case CK_BooleanToSignedIntegral: case CK_NullToPointer: case CK_IntegralToPointer: case CK_PointerToIntegral: @@ -345,6 +345,17 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, // Delegate to SValBuilder to process. SVal V = state->getSVal(Ex, LCtx); V = svalBuilder.evalCast(V, T, ExTy); + // Negate the result if we're treating the boolean as a signed i1 + if (CastE->getCastKind() == CK_BooleanToSignedIntegral) + V = evalMinus(V); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } + case CK_IntegralCast: { + // Delegate to SValBuilder to process. + SVal V = state->getSVal(Ex, LCtx); + V = svalBuilder.evalIntegralCast(state, V, T, ExTy); state = state->BindExpr(CastE, LCtx, V); Bldr.generateNode(CastE, Pred, state); continue; diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index ad3f396e39a1..30052ccacee4 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -245,7 +245,7 @@ QualType CXXBaseObjectRegion::getValueType() const { // FoldingSet profiling. //===----------------------------------------------------------------------===// -void MemSpaceRegion::Profile(llvm::FoldingSetNodeID& ID) const { +void MemSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger((unsigned)getKind()); } @@ -357,31 +357,31 @@ void ElementRegion::Profile(llvm::FoldingSetNodeID& ID) const { ElementRegion::ProfileRegion(ID, ElementType, Index, superRegion); } -void FunctionTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, +void FunctionCodeRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const NamedDecl *FD, const MemRegion*) { - ID.AddInteger(MemRegion::FunctionTextRegionKind); + ID.AddInteger(MemRegion::FunctionCodeRegionKind); ID.AddPointer(FD); } -void FunctionTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { - FunctionTextRegion::ProfileRegion(ID, FD, superRegion); +void FunctionCodeRegion::Profile(llvm::FoldingSetNodeID& ID) const { + FunctionCodeRegion::ProfileRegion(ID, FD, superRegion); } -void BlockTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, +void BlockCodeRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const BlockDecl *BD, CanQualType, const AnalysisDeclContext *AC, const MemRegion*) { - ID.AddInteger(MemRegion::BlockTextRegionKind); + ID.AddInteger(MemRegion::BlockCodeRegionKind); ID.AddPointer(BD); } -void BlockTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { - BlockTextRegion::ProfileRegion(ID, BD, locTy, AC, superRegion); +void BlockCodeRegion::Profile(llvm::FoldingSetNodeID& ID) const { + BlockCodeRegion::ProfileRegion(ID, BD, locTy, AC, superRegion); } void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, - const BlockTextRegion *BC, + const BlockCodeRegion *BC, const LocationContext *LC, unsigned BlkCount, const MemRegion *sReg) { @@ -457,11 +457,11 @@ void AllocaRegion::dumpToStream(raw_ostream &os) const { os << "alloca{" << (const void*) Ex << ',' << Cnt << '}'; } -void FunctionTextRegion::dumpToStream(raw_ostream &os) const { +void FunctionCodeRegion::dumpToStream(raw_ostream &os) const { os << "code{" << getDecl()->getDeclName().getAsString() << '}'; } -void BlockTextRegion::dumpToStream(raw_ostream &os) const { +void BlockCodeRegion::dumpToStream(raw_ostream &os) const { os << "block_code{" << (const void*) this << '}'; } @@ -533,6 +533,10 @@ void RegionRawOffset::dumpToStream(raw_ostream &os) const { os << "raw_offset{" << getRegion() << ',' << getOffset().getQuantity() << '}'; } +void CodeSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "CodeSpaceRegion"; +} + void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { os << "StaticGlobalsMemSpace{" << CR << '}'; } @@ -711,11 +715,11 @@ const HeapSpaceRegion *MemRegionManager::getHeapRegion() { return LazyAllocate(heap); } -const MemSpaceRegion *MemRegionManager::getUnknownRegion() { +const UnknownSpaceRegion *MemRegionManager::getUnknownRegion() { return LazyAllocate(unknown); } -const MemSpaceRegion *MemRegionManager::getCodeRegion() { +const CodeSpaceRegion *MemRegionManager::getCodeRegion() { return LazyAllocate(code); } @@ -815,11 +819,11 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, const Decl *STCD = STC->getDecl(); if (isa<FunctionDecl>(STCD) || isa<ObjCMethodDecl>(STCD)) sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, - getFunctionTextRegion(cast<NamedDecl>(STCD))); + getFunctionCodeRegion(cast<NamedDecl>(STCD))); else if (const BlockDecl *BD = dyn_cast<BlockDecl>(STCD)) { // FIXME: The fallback type here is totally bogus -- though it should // never be queried, it will prevent uniquing with the real - // BlockTextRegion. Ideally we'd fix the AST so that we always had a + // BlockCodeRegion. Ideally we'd fix the AST so that we always had a // signature. QualType T; if (const TypeSourceInfo *TSI = BD->getSignatureAsWritten()) @@ -830,8 +834,8 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, T = getContext().getFunctionNoProtoType(T); T = getContext().getBlockPointerType(T); - const BlockTextRegion *BTR = - getBlockTextRegion(BD, C.getCanonicalType(T), + const BlockCodeRegion *BTR = + getBlockCodeRegion(BD, C.getCanonicalType(T), STC->getAnalysisDeclContext()); sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, BTR); @@ -852,7 +856,7 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, } const BlockDataRegion * -MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC, +MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC, const LocationContext *LC, unsigned blockCount) { const MemRegion *sReg = nullptr; @@ -925,15 +929,15 @@ MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, return R; } -const FunctionTextRegion * -MemRegionManager::getFunctionTextRegion(const NamedDecl *FD) { - return getSubRegion<FunctionTextRegion>(FD, getCodeRegion()); +const FunctionCodeRegion * +MemRegionManager::getFunctionCodeRegion(const NamedDecl *FD) { + return getSubRegion<FunctionCodeRegion>(FD, getCodeRegion()); } -const BlockTextRegion * -MemRegionManager::getBlockTextRegion(const BlockDecl *BD, CanQualType locTy, +const BlockCodeRegion * +MemRegionManager::getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy, AnalysisDeclContext *AC) { - return getSubRegion<BlockTextRegion>(BD, locTy, AC, getCodeRegion()); + return getSubRegion<BlockCodeRegion>(BD, locTy, AC, getCodeRegion()); } @@ -1196,7 +1200,7 @@ RegionOffset MemRegion::getAsOffset() const { while (1) { switch (R->getKind()) { - case GenericMemSpaceRegionKind: + case CodeSpaceRegionKind: case StackLocalsSpaceRegionKind: case StackArgumentsSpaceRegionKind: case HeapSpaceRegionKind: @@ -1209,8 +1213,8 @@ RegionOffset MemRegion::getAsOffset() const { assert(Offset == 0 && !SymbolicOffsetBase); goto Finish; - case FunctionTextRegionKind: - case BlockTextRegionKind: + case FunctionCodeRegionKind: + case BlockCodeRegionKind: case BlockDataRegionKind: // These will never have bindings, but may end up having values requested // if the user does some strange casting. diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 4f9ad9ebccd9..100fa75c5f42 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -536,19 +536,19 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) { // TODO: should be rewritten using SymExpr::symbol_iterator. switch (sym->getKind()) { - case SymExpr::RegionValueKind: - case SymExpr::ConjuredKind: - case SymExpr::DerivedKind: - case SymExpr::ExtentKind: - case SymExpr::MetadataKind: + case SymExpr::SymbolRegionValueKind: + case SymExpr::SymbolConjuredKind: + case SymExpr::SymbolDerivedKind: + case SymExpr::SymbolExtentKind: + case SymExpr::SymbolMetadataKind: break; - case SymExpr::CastSymbolKind: + case SymExpr::SymbolCastKind: return scan(cast<SymbolCast>(sym)->getOperand()); - case SymExpr::SymIntKind: + case SymExpr::SymIntExprKind: return scan(cast<SymIntExpr>(sym)->getLHS()); - case SymExpr::IntSymKind: + case SymExpr::IntSymExprKind: return scan(cast<IntSymExpr>(sym)->getRHS()); - case SymExpr::SymSymKind: { + case SymExpr::SymSymExprKind: { const SymSymExpr *x = cast<SymSymExpr>(sym); return scan(x->getLHS()) && scan(x->getRHS()); } diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 0a2b2e64a142..77b0ad32b6b7 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -171,7 +171,7 @@ private: case APSIntType::RTR_Below: // The entire range is outside the symbol's set of possible values. // If this is a conventionally-ordered range, the state is infeasible. - if (Lower < Upper) + if (Lower <= Upper) return false; // However, if the range wraps around, it spans all possible values. @@ -222,7 +222,7 @@ private: case APSIntType::RTR_Above: // The entire range is outside the symbol's set of possible values. // If this is a conventionally-ordered range, the state is infeasible. - if (Lower < Upper) + if (Lower <= Upper) return false; // However, if the range wraps around, it spans all possible values. diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index cdae04068e1d..18315225a99d 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -214,15 +214,15 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, } DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) { - return loc::MemRegionVal(MemMgr.getFunctionTextRegion(func)); + return loc::MemRegionVal(MemMgr.getFunctionCodeRegion(func)); } DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block, CanQualType locTy, const LocationContext *locContext, unsigned blockCount) { - const BlockTextRegion *BC = - MemMgr.getBlockTextRegion(block, locTy, locContext->getAnalysisDeclContext()); + const BlockCodeRegion *BC = + MemMgr.getBlockCodeRegion(block, locTy, locContext->getAnalysisDeclContext()); const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, locContext, blockCount); return loc::MemRegionVal(BD); @@ -423,6 +423,45 @@ static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy, return true; } +// Handles casts of type CK_IntegralCast. +// At the moment, this function will redirect to evalCast, except when the range +// of the original value is known to be greater than the max of the target type. +SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, + QualType castTy, QualType originalTy) { + + // No truncations if target type is big enough. + if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy)) + return evalCast(val, castTy, originalTy); + + const SymExpr *se = val.getAsSymbolicExpression(); + if (!se) // Let evalCast handle non symbolic expressions. + return evalCast(val, castTy, originalTy); + + // Find the maximum value of the target type. + APSIntType ToType(getContext().getTypeSize(castTy), + castTy->isUnsignedIntegerType()); + llvm::APSInt ToTypeMax = ToType.getMaxValue(); + NonLoc ToTypeMaxVal = + makeIntVal(ToTypeMax.isUnsigned() ? ToTypeMax.getZExtValue() + : ToTypeMax.getSExtValue(), + castTy) + .castAs<NonLoc>(); + // Check the range of the symbol being casted against the maximum value of the + // target type. + NonLoc FromVal = val.castAs<NonLoc>(); + QualType CmpTy = getConditionType(); + NonLoc CompVal = + evalBinOpNN(state, BO_LT, FromVal, ToTypeMaxVal, CmpTy).castAs<NonLoc>(); + ProgramStateRef IsNotTruncated, IsTruncated; + std::tie(IsNotTruncated, IsTruncated) = state->assume(CompVal); + if (!IsNotTruncated && IsTruncated) { + // Symbol is truncated so we evaluate it as a cast. + NonLoc CastVal = makeNonLoc(se, originalTy, castTy); + return CastVal; + } + return evalCast(val, castTy, originalTy); +} + // FIXME: should rewrite according to the cast kind. SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { castTy = Context.getCanonicalType(castTy); diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index 8de939f47d86..dffee6c8c57b 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -51,7 +51,7 @@ bool SVal::hasConjuredSymbol() const { const FunctionDecl *SVal::getAsFunctionDecl() const { if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { const MemRegion* R = X->getRegion(); - if (const FunctionTextRegion *CTR = R->getAs<FunctionTextRegion>()) + if (const FunctionCodeRegion *CTR = R->getAs<FunctionCodeRegion>()) if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CTR->getDecl())) return FD; } @@ -240,7 +240,7 @@ void SVal::dump() const { dumpToStream(llvm::errs()); } void SVal::dumpToStream(raw_ostream &os) const { switch (getBaseKind()) { - case UnknownKind: + case UnknownValKind: os << "Unknown"; break; case NonLocKind: @@ -249,7 +249,7 @@ void SVal::dumpToStream(raw_ostream &os) const { case LocKind: castAs<Loc>().dumpToStream(os); break; - case UndefinedKind: + case UndefinedValKind: os << "Undefined"; break; } @@ -313,7 +313,7 @@ void Loc::dumpToStream(raw_ostream &os) const { case loc::GotoLabelKind: os << "&&" << castAs<loc::GotoLabel>().getLabel()->getName(); break; - case loc::MemRegionKind: + case loc::MemRegionValKind: os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString(); break; default: diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index a704ce224554..72b852b2e21d 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -141,9 +141,9 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { // unless this is a weak function or a symbolic region. if (castTy->isBooleanType()) { switch (val.getSubKind()) { - case loc::MemRegionKind: { + case loc::MemRegionValKind: { const MemRegion *R = val.castAs<loc::MemRegionVal>().getRegion(); - if (const FunctionTextRegion *FTR = dyn_cast<FunctionTextRegion>(R)) + if (const FunctionCodeRegion *FTR = dyn_cast<FunctionCodeRegion>(R)) if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FTR->getDecl())) if (FD->isWeak()) // FIXME: Currently we are using an extent symbol here, @@ -689,7 +689,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // completely unknowable. return UnknownVal(); } - case loc::MemRegionKind: { + case loc::MemRegionValKind: { if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { // If one of the operands is a symbol and the other is a constant, // build an expression for use by the constraint manager. @@ -718,7 +718,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // Get both values as regions, if possible. const MemRegion *LeftMR = lhs.getAsRegion(); - assert(LeftMR && "MemRegionKind SVal doesn't have a region!"); + assert(LeftMR && "MemRegionValKind SVal doesn't have a region!"); const MemRegion *RightMR = rhs.getAsRegion(); if (!RightMR) diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 7cdb55a59782..de29f0eedd12 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -100,7 +100,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) // Process region cast according to the kind of the region being cast. switch (R->getKind()) { case MemRegion::CXXThisRegionKind: - case MemRegion::GenericMemSpaceRegionKind: + case MemRegion::CodeSpaceRegionKind: case MemRegion::StackLocalsSpaceRegionKind: case MemRegion::StackArgumentsSpaceRegionKind: case MemRegion::HeapSpaceRegionKind: @@ -112,8 +112,8 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) llvm_unreachable("Invalid region cast"); } - case MemRegion::FunctionTextRegionKind: - case MemRegion::BlockTextRegionKind: + case MemRegion::FunctionCodeRegionKind: + case MemRegion::BlockCodeRegionKind: case MemRegion::BlockDataRegionKind: case MemRegion::StringRegionKind: // FIXME: Need to handle arbitrary downcasts. @@ -393,7 +393,7 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { const MemRegion* BaseR = nullptr; switch (BaseL.getSubKind()) { - case loc::MemRegionKind: + case loc::MemRegionValKind: BaseR = BaseL.castAs<loc::MemRegionVal>().getRegion(); break; diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index 99b2e147cb49..2dd252c223fd 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -115,22 +115,22 @@ void SymExpr::symbol_iterator::expand() { const SymExpr *SE = itr.pop_back_val(); switch (SE->getKind()) { - case SymExpr::RegionValueKind: - case SymExpr::ConjuredKind: - case SymExpr::DerivedKind: - case SymExpr::ExtentKind: - case SymExpr::MetadataKind: + case SymExpr::SymbolRegionValueKind: + case SymExpr::SymbolConjuredKind: + case SymExpr::SymbolDerivedKind: + case SymExpr::SymbolExtentKind: + case SymExpr::SymbolMetadataKind: return; - case SymExpr::CastSymbolKind: + case SymExpr::SymbolCastKind: itr.push_back(cast<SymbolCast>(SE)->getOperand()); return; - case SymExpr::SymIntKind: + case SymExpr::SymIntExprKind: itr.push_back(cast<SymIntExpr>(SE)->getLHS()); return; - case SymExpr::IntSymKind: + case SymExpr::IntSymExprKind: itr.push_back(cast<IntSymExpr>(SE)->getRHS()); return; - case SymExpr::SymSymKind: { + case SymExpr::SymSymExprKind: { const SymSymExpr *x = cast<SymSymExpr>(SE); itr.push_back(x->getLHS()); itr.push_back(x->getRHS()); @@ -458,35 +458,35 @@ bool SymbolReaper::isLive(SymbolRef sym) { bool KnownLive; switch (sym->getKind()) { - case SymExpr::RegionValueKind: + case SymExpr::SymbolRegionValueKind: KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion()); break; - case SymExpr::ConjuredKind: + case SymExpr::SymbolConjuredKind: KnownLive = false; break; - case SymExpr::DerivedKind: + case SymExpr::SymbolDerivedKind: KnownLive = isLive(cast<SymbolDerived>(sym)->getParentSymbol()); break; - case SymExpr::ExtentKind: + case SymExpr::SymbolExtentKind: KnownLive = isLiveRegion(cast<SymbolExtent>(sym)->getRegion()); break; - case SymExpr::MetadataKind: + case SymExpr::SymbolMetadataKind: KnownLive = MetadataInUse.count(sym) && isLiveRegion(cast<SymbolMetadata>(sym)->getRegion()); if (KnownLive) MetadataInUse.erase(sym); break; - case SymExpr::SymIntKind: + case SymExpr::SymIntExprKind: KnownLive = isLive(cast<SymIntExpr>(sym)->getLHS()); break; - case SymExpr::IntSymKind: + case SymExpr::IntSymExprKind: KnownLive = isLive(cast<IntSymExpr>(sym)->getRHS()); break; - case SymExpr::SymSymKind: + case SymExpr::SymSymExprKind: KnownLive = isLive(cast<SymSymExpr>(sym)->getLHS()) && isLive(cast<SymSymExpr>(sym)->getRHS()); break; - case SymExpr::CastSymbolKind: + case SymExpr::SymbolCastKind: KnownLive = isLive(cast<SymbolCast>(sym)->getOperand()); break; } diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index bf85c4ca0c60..d1446855e01f 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -496,10 +496,11 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) { (Mgr->options.InliningMode == All ? nullptr : &VisitedCallees)); // Add the visited callees to the global visited set. - for (SetOfConstDecls::iterator I = VisitedCallees.begin(), - E = VisitedCallees.end(); I != E; ++I) { - Visited.insert(*I); - } + for (const Decl *Callee : VisitedCallees) + // Decls from CallGraph are already canonical. But Decls coming from + // CallExprs may be not. We should canonicalize them manually. + Visited.insert(isa<ObjCMethodDecl>(Callee) ? Callee + : Callee->getCanonicalDecl()); VisitedAsTopLevel.insert(D); } } diff --git a/test/Analysis/inlining/analysis-order.c b/test/Analysis/inlining/analysis-order.c new file mode 100644 index 000000000000..5149818c74fd --- /dev/null +++ b/test/Analysis/inlining/analysis-order.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core.builtin.NoReturnFunctions -analyzer-display-progress %s 2>&1 | FileCheck %s + +// Do not analyze test1() again because it was inlined +void test1(); + +void test2() { + test1(); +} + +void test1() { +} + +// CHECK: analysis-order.c test2 +// CHECK-NEXT: analysis-order.c test1 +// CHECK-NEXT: analysis-order.c test2 diff --git a/test/Analysis/range_casts.c b/test/Analysis/range_casts.c new file mode 100644 index 000000000000..682369cce66f --- /dev/null +++ b/test/Analysis/range_casts.c @@ -0,0 +1,156 @@ +// This test checks that intersecting ranges does not cause 'system is over constrained' assertions in the case of eg: 32 bits unsigned integers getting their range from 64 bits signed integers. +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-store=region -verify %s + +void clang_analyzer_warnIfReached(); + +void f1(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index + 1 == 0) // because of foo range, index is in range [0; UINT_MAX] + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f2(unsigned long foo) +{ + int index = -1; + if (index < foo) index = foo; // index equals ULONG_MAX + if (index + 1 == 0) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // no-warning +} + +void f3(unsigned long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index + 1 == 0) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f4(long foo) +{ + int index = -1; + if (index < foo) index = foo; + if (index + 1 == 0) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f5(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index == -1) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f6(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index == -1) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f7(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index - 1 == 0) // Was not reached prior fix. + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f8(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index + 1L == 0L) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f9(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index - 1L == 0L) // Was not reached prior fix. + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f10(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index + 1 == 0L) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f11(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index + 1UL == 0L) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f12(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + if (index - 1UL == 0L) // Was not reached prior fix. + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f13(int foo) +{ + unsigned short index = -1; + if (index < foo) index = foo; + if (index + 1 == 0) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f14(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + long bar = foo; + if (index + 1 == 0) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} + +void f15(long foo) +{ + unsigned index = -1; + if (index < foo) index = foo; + unsigned int tmp = index + 1; + if (tmp == 0) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} diff --git a/test/CXX/class/class.mem/p13.cpp b/test/CXX/class/class.mem/p13.cpp index 1d7b9bc81551..bc01fd4d30c5 100644 --- a/test/CXX/class/class.mem/p13.cpp +++ b/test/CXX/class/class.mem/p13.cpp @@ -58,12 +58,12 @@ struct X3a { }; // - every member of every anonymous union that is a member of class T. -struct X4 { +struct X4 { // expected-note{{previous}} union { int X; union { float Y; - unsigned X4; // expected-error{{member 'X4' has the same name as its class}} + unsigned X4; // expected-error{{redeclares 'X4'}} }; }; }; diff --git a/test/CXX/class/class.union/class.union.anon/p1.cpp b/test/CXX/class/class.union/class.union.anon/p1.cpp new file mode 100644 index 000000000000..31c9313cf53a --- /dev/null +++ b/test/CXX/class/class.union/class.union.anon/p1.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -verify %s + +struct X { + int a; // expected-note {{previous}} + void b(); // expected-note {{previous}} + struct c; // expected-note {{previous}} + typedef int d; // expected-note {{previous}} + + union { + int a; // expected-error {{member of anonymous union redeclares}} + int b; // expected-error {{member of anonymous union redeclares}} + int c; // expected-error {{member of anonymous union redeclares}} + int d; // expected-error {{member of anonymous union redeclares}} + int e; // expected-note {{previous}} + int f; // expected-note {{previous}} + int g; // expected-note {{previous}} + int h; // expected-note {{previous}} + }; + + int e; // expected-error {{duplicate member}} + void f(); // expected-error {{redefinition}} + struct g; // expected-error {{redefinition}} + typedef int h; // expected-error {{redefinition}} +}; diff --git a/test/CXX/class/class.union/p8.cpp b/test/CXX/class/class.union/class.union.anon/p4.cpp index cc54ba406619..cc54ba406619 100644 --- a/test/CXX/class/class.union/p8.cpp +++ b/test/CXX/class/class.union/class.union.anon/p4.cpp diff --git a/test/CXX/temp/temp.res/temp.local/p6.cpp b/test/CXX/temp/temp.res/temp.local/p6.cpp index 06eb1bef7fea..843b45543fcf 100644 --- a/test/CXX/temp/temp.res/temp.local/p6.cpp +++ b/test/CXX/temp/temp.res/temp.local/p6.cpp @@ -5,7 +5,7 @@ namespace N {} template<typename T, // expected-note {{declared here}} typename T> struct X {}; // expected-error {{declaration of 'T' shadows template parameter}} -template<typename T> struct Y { // expected-note 16{{declared here}} +template<typename T> struct Y { // expected-note 17{{declared here}} template<typename T> struct A {}; // expected-error {{declaration of 'T' shadows template parameter}} struct B { @@ -56,9 +56,74 @@ template<typename T> struct Y { // expected-note 16{{declared here}} namespace T = N; // expected-error {{declaration of 'T' shadows template parameter}} } + // FIXME: These diagnostics are poorly worded. Lookup for the elaborated type + // specifier finds the template parameter in this case, which is ill-formed + // because it's not a struct. + void f() { + struct T *p; // expected-error {{declaration of 'T' shadows template parameter}} + } friend struct T; // expected-error {{declaration of 'T' shadows template parameter}} }; +template<int T> struct Z { // expected-note 15{{declared here}} + template<typename T> struct A {}; // expected-error {{declaration of 'T' shadows template parameter}} + + struct B { + template<typename> struct T {}; // FIXME: desired-error {{declaration of 'T' shadows template parameter}} + }; + struct C { + template<typename> void T(); // expected-error {{declaration of 'T' shadows template parameter}} + }; + struct D { + struct T {}; // expected-error {{declaration of 'T' shadows template parameter}} + }; + struct E { + typedef int T; // expected-error {{declaration of 'T' shadows template parameter}} + }; + struct F { + using T = int; // expected-error {{declaration of 'T' shadows template parameter}} + }; + struct G { + int T; // expected-error {{declaration of 'T' shadows template parameter}} + }; + struct H { + static int T; // expected-error {{declaration of 'T' shadows template parameter}} + }; + struct I { + void T(); // expected-error {{declaration of 'T' shadows template parameter}} + }; + struct J { + enum T { e }; // expected-error {{declaration of 'T' shadows template parameter}} + }; + struct K { + enum E { T }; // expected-error {{declaration of 'T' shadows template parameter}} + }; + + void a() { + extern int T; // expected-error {{declaration of 'T' shadows template parameter}} + } + void b() { + int T; // expected-error {{declaration of 'T' shadows template parameter}} + } + void c() { + try {} + catch (int T) {} // expected-error {{declaration of 'T' shadows template parameter}} + } + void d() { + void T(); // expected-error {{declaration of 'T' shadows template parameter}} + } + void e() { + namespace T = N; // expected-error {{declaration of 'T' shadows template parameter}} + } + + // These cases are valid when 'T' is a non-type template parameter, as T + // names an injected struct ::T, which doesn't shadow the template parameter. + void f() { + struct T *p; + } + friend struct T; +}; + template<typename T> // expected-note {{declared here}} void f(int T) {} // expected-error {{declaration of 'T' shadows template parameter}} diff --git a/test/CodeGen/builtins-ppc-vsx.c b/test/CodeGen/builtins-ppc-vsx.c index 9a40d3041db1..15f98b57a513 100644 --- a/test/CodeGen/builtins-ppc-vsx.c +++ b/test/CodeGen/builtins-ppc-vsx.c @@ -845,4 +845,51 @@ void test1() { // CHECK: xor <2 x i64> // CHECK-LE: xor <2 x i64> + res_vsll = vec_cts(vd, 0); +// CHECK: fmul <2 x double> +// CHECK: fptosi <2 x double> %{{.*}} to <2 x i64> +// CHECK-LE: fmul <2 x double> +// CHECK-LE: fptosi <2 x double> %{{.*}} to <2 x i64> + + res_vsll = vec_cts(vd, 31); +// CHECK: fmul <2 x double> +// CHECK: fptosi <2 x double> %{{.*}} to <2 x i64> +// CHECK-LE: fmul <2 x double> +// CHECK-LE: fptosi <2 x double> %{{.*}} to <2 x i64> + + res_vsll = vec_ctu(vd, 0); +// CHECK: fmul <2 x double> +// CHECK: fptoui <2 x double> %{{.*}} to <2 x i64> +// CHECK-LE: fmul <2 x double> +// CHECK-LE: fptoui <2 x double> %{{.*}} to <2 x i64> + + res_vsll = vec_ctu(vd, 31); +// CHECK: fmul <2 x double> +// CHECK: fptoui <2 x double> %{{.*}} to <2 x i64> +// CHECK-LE: fmul <2 x double> +// CHECK-LE: fptoui <2 x double> %{{.*}} to <2 x i64> + + res_vd = vec_ctf(vsll, 0); +// CHECK: sitofp <2 x i64> %{{.*}} to <2 x double> +// CHECK: fmul <2 x double> +// CHECK-LE: sitofp <2 x i64> %{{.*}} to <2 x double> +// CHECK-LE: fmul <2 x double> + + res_vd = vec_ctf(vsll, 31); +// CHECK: sitofp <2 x i64> %{{.*}} to <2 x double> +// CHECK: fmul <2 x double> +// CHECK-LE: sitofp <2 x i64> %{{.*}} to <2 x double> +// CHECK-LE: fmul <2 x double> + + res_vd = vec_ctf(vull, 0); +// CHECK: uitofp <2 x i64> %{{.*}} to <2 x double> +// CHECK: fmul <2 x double> +// CHECK-LE: uitofp <2 x i64> %{{.*}} to <2 x double> +// CHECK-LE: fmul <2 x double> + + res_vd = vec_ctf(vull, 31); +// CHECK: uitofp <2 x i64> %{{.*}} to <2 x double> +// CHECK: fmul <2 x double> +// CHECK-LE: uitofp <2 x i64> %{{.*}} to <2 x double> +// CHECK-LE: fmul <2 x double> } diff --git a/test/CodeGen/target-data.c b/test/CodeGen/target-data.c index 08265f9da365..2ed7f0916fce 100644 --- a/test/CodeGen/target-data.c +++ b/test/CodeGen/target-data.c @@ -80,11 +80,11 @@ // RUN: %clang_cc1 -triple wasm32-unknown-unknown -o - -emit-llvm %s | \ // RUN: FileCheck %s -check-prefix=WEBASSEMBLY32 -// WEBASSEMBLY32: target datalayout = "e-p:32:32-i64:64-n32:64-S128" +// WEBASSEMBLY32: target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" // RUN: %clang_cc1 -triple wasm64-unknown-unknown -o - -emit-llvm %s | \ // RUN: FileCheck %s -check-prefix=WEBASSEMBLY64 -// WEBASSEMBLY64: target datalayout = "e-p:64:64-i64:64-n32:64-S128" +// WEBASSEMBLY64: target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128" // RUN: %clang_cc1 -triple powerpc-unknown -o - -emit-llvm %s | \ // RUN: FileCheck %s -check-prefix=PPC diff --git a/test/CodeGenCXX/builtins-systemz-zvector.cpp b/test/CodeGenCXX/builtins-systemz-zvector.cpp new file mode 100644 index 000000000000..aedb30ffbb4c --- /dev/null +++ b/test/CodeGenCXX/builtins-systemz-zvector.cpp @@ -0,0 +1,50 @@ +// REQUIRES: systemz-registered-target +// RUN: %clang_cc1 -target-cpu z13 -triple s390x-linux-gnu \ +// RUN: -fzvector -fno-lax-vector-conversions -std=c++11 \ +// RUN: -Wall -Wno-unused -Werror -emit-llvm %s -o - | FileCheck %s + +bool gb; + +// There was an issue where we weren't properly converting constexprs to +// vectors with elements of the appropriate width. (e.g. +// (vector signed short)0 would be lowered as [4 x i32] in some cases) + +// CHECK-LABEL: @_Z8testIntsDv4_i +void testInts(vector int VI) { + constexpr vector int CI1 = (vector int)0LL; + // CHECK: icmp + gb = (VI == CI1)[0]; + + // Likewise for float inits. + constexpr vector int CI2 = (vector int)char(0); + // CHECK: icmp + gb = (VI == CI2)[0]; + + constexpr vector int CF1 = (vector int)0.0; + // CHECK: icmp + gb = (VI == CF1)[0]; + + constexpr vector int CF2 = (vector int)0.0f; + // CHECK: icmp + gb = (VI == CF2)[0]; +} + +// CHECK-LABEL: @_Z10testFloatsDv2_d +void testFloats(vector double VD) { + constexpr vector double CI1 = (vector double)0LL; + // CHECK: fcmp + gb = (VD == CI1)[0]; + + // Likewise for float inits. + constexpr vector double CI2 = (vector double)char(0); + // CHECK: fcmp + gb = (VD == CI2)[0]; + + constexpr vector double CF1 = (vector double)0.0; + // CHECK: fcmp + gb = (VD == CF1)[0]; + + constexpr vector double CF2 = (vector double)0.0f; + // CHECK: fcmp + gb = (VD == CF2)[0]; +} diff --git a/test/CodeGenCXX/mangle-ms.cpp b/test/CodeGenCXX/mangle-ms.cpp index c2a311423a9d..c82fca49f613 100644 --- a/test/CodeGenCXX/mangle-ms.cpp +++ b/test/CodeGenCXX/mangle-ms.cpp @@ -454,3 +454,28 @@ namespace Complex { // CHECK-DAG: define void @"\01?f@Complex@@YAXU?$_Complex@H@__clang@@@Z"( void f(_Complex int) {} } + +namespace PR26029 { +template <class> +struct L { + L() {} +}; +template <class> +class H; +struct M : L<H<int *> > {}; + +template <class> +struct H {}; + +template <class GT> +void m_fn3() { + (H<GT *>()); + M(); +} + +void runOnFunction() { + L<H<int *> > b; + m_fn3<int>(); +} +// CHECK-DAG: call {{.*}} @"\01??0?$L@V?$H@PAH@PR26029@@@PR26029@@QAE@XZ" +} diff --git a/test/CodeGenCXX/pass-object-size.cpp b/test/CodeGenCXX/pass-object-size.cpp index 254669b97627..2c7f9742a8e6 100644 --- a/test/CodeGenCXX/pass-object-size.cpp +++ b/test/CodeGenCXX/pass-object-size.cpp @@ -25,3 +25,21 @@ void Lambdas(char *ptr) { // CHECK-DAG: define internal i64 @"_ZZN7lambdas7LambdasEPcENK3$_1clEPvU17pass_object_size0" // CHECK-NOT: call i64 @llvm.objectsize } + +// This is here instead of in Sema/ because we need to check to make sure the +// proper function is called. If it's not, we'll end up with assertion errors. +namespace addrof { +void OvlFoo(void *const __attribute__((pass_object_size(0)))) {} +void OvlFoo(int *const) {} + +// CHECK: define void @_ZN6addrof4TestEv +void Test() { + // Treating parens-only calls as though they were direct is consistent with + // how we handle other implicitly unaddressable functions (e.g. builtins). + // CHECK: call void @_ZN6addrof6OvlFooEPvU17pass_object_size0 + (OvlFoo)(nullptr); + + // CHECK: call void @_ZN6addrof6OvlFooEPi + (&OvlFoo)(nullptr); +} +} diff --git a/test/CodeGenCXX/vector-splat-conversion.cpp b/test/CodeGenCXX/vector-splat-conversion.cpp index 410df3dd0cef..805f9f5bab15 100644 --- a/test/CodeGenCXX/vector-splat-conversion.cpp +++ b/test/CodeGenCXX/vector-splat-conversion.cpp @@ -1,19 +1,51 @@ // RUN: %clang_cc1 %s -triple arm64-apple-ios8.1.0 -std=c++11 -emit-llvm -o - | FileCheck %s -// rdar://20000762 typedef __attribute__((__ext_vector_type__(8))) float vector_float8; typedef vector_float8 float8; -void MandelbrotPolyCalcSIMD8() -{ - constexpr float8 v4 = 4.0; // value to compare against abs(z)^2, to see if bounded - float8 vABS; - auto vLT = vABS < v4; +// rdar://20000762 +// CHECK-LABEL: define void @_Z23MandelbrotPolyCalcSIMD8v +void MandelbrotPolyCalcSIMD8() { + constexpr float8 v4 = 4.0; // value to compare against abs(z)^2, to see if bounded + float8 vABS; + auto vLT = vABS < v4; + // CHECK: store <8 x float> + // CHECK: [[ZERO:%.*]] = load <8 x float>, <8 x float>* [[VARBS:%.*]] + // CHECK: [[CMP:%.*]] = fcmp olt <8 x float> [[ZERO]] + // CHECK: [[SEXT:%.*]] = sext <8 x i1> [[CMP]] to <8 x i32> + // CHECK: store <8 x i32> [[SEXT]], <8 x i32>* [[VLT:%.*]] } -// CHECK: store <8 x float> -// CHECK: [[ZERO:%.*]] = load <8 x float>, <8 x float>* [[VARBS:%.*]] -// CHECK: [[CMP:%.*]] = fcmp olt <8 x float> [[ZERO]] -// CHECK: [[SEXT:%.*]] = sext <8 x i1> [[CMP]] to <8 x i32> -// CHECK: store <8 x i32> [[SEXT]], <8 x i32>* [[VLT:%.*]] +typedef __attribute__((__ext_vector_type__(4))) int int4; +typedef __attribute__((__ext_vector_type__(4))) float float4; +typedef __attribute__((__ext_vector_type__(4))) __int128 bigint4; + +// CHECK-LABEL: define void @_Z14BoolConversionv +void BoolConversion() { + // CHECK: store <4 x i32> <i32 -1, i32 -1, i32 -1, i32 -1> + int4 intsT = (int4)true; + // CHECK: store <4 x i32> zeroinitializer + int4 intsF = (int4)false; + // CHECK: store <4 x float> <float -1.000000e+00, float -1.000000e+00, float -1.000000e+00, float -1.000000e+00> + float4 floatsT = (float4)true; + // CHECK: store <4 x float> zeroinitializer + float4 floatsF = (float4)false; + // CHECK: store <4 x i128> <i128 -1, i128 -1, i128 -1, i128 -1> + bigint4 bigintsT = (bigint4)true; + // CHECK: store <4 x i128> zeroinitializer + bigint4 bigintsF = (bigint4)false; + + // CHECK: store <4 x i32> <i32 -1, i32 -1, i32 -1, i32 -1> + constexpr int4 cIntsT = (int4)true; + // CHECK: store <4 x i32> zeroinitializer + constexpr int4 cIntsF = (int4)false; + // CHECK: store <4 x float> <float -1.000000e+00, float -1.000000e+00, float -1.000000e+00, float -1.000000e+00> + constexpr float4 cFloatsT = (float4)true; + // CHECK: store <4 x float> zeroinitializer + constexpr float4 cFloatsF = (float4)false; + // CHECK: store <4 x i128> <i128 -1, i128 -1, i128 -1, i128 -1> + constexpr bigint4 cBigintsT = (bigint4)true; + // CHECK: store <4 x i128> zeroinitializer + constexpr bigint4 cBigintsF = (bigint4)false; +} diff --git a/test/CodeGenOpenCL/bool_cast.cl b/test/CodeGenOpenCL/bool_cast.cl index d63431b1b7ca..8c86b06577d6 100644 --- a/test/CodeGenOpenCL/bool_cast.cl +++ b/test/CodeGenOpenCL/bool_cast.cl @@ -2,7 +2,9 @@ typedef unsigned char uchar4 __attribute((ext_vector_type(4))); typedef unsigned int int4 __attribute((ext_vector_type(4))); +typedef float float4 __attribute((ext_vector_type(4))); +// CHECK-LABEL: define void @ker() void kernel ker() { bool t = true; int4 vec4 = (int4)t; @@ -24,4 +26,8 @@ void kernel ker() { unsigned char c; c = (unsigned char)true; // CHECK: store i8 1, i8* %c, align 1 + + float4 vf; + vf = (float4)true; +// CHECK: store <4 x float> <float -1.000000e+00, float -1.000000e+00, float -1.000000e+00, float -1.000000e+00> } diff --git a/test/CodeGenOpenCL/pipe_types.cl b/test/CodeGenOpenCL/pipe_types.cl new file mode 100644 index 000000000000..547071cf85a3 --- /dev/null +++ b/test/CodeGenOpenCL/pipe_types.cl @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -emit-llvm -O0 -cl-std=CL2.0 -o - %s | FileCheck %s + +// CHECK: %opencl.pipe_t = type opaque +typedef unsigned char __attribute__((ext_vector_type(3))) uchar3; +typedef int __attribute__((ext_vector_type(4))) int4; + +void test1(read_only pipe int p) { +// CHECK: define void @test1(%opencl.pipe_t* %p) + reserve_id_t rid; +// CHECK: %rid = alloca %opencl.reserve_id_t +} + +void test2(write_only pipe float p) { +// CHECK: define void @test2(%opencl.pipe_t* %p) +} + +void test3(read_only pipe const int p) { +// CHECK: define void @test3(%opencl.pipe_t* %p) +} + +void test4(read_only pipe uchar3 p) { +// CHECK: define void @test4(%opencl.pipe_t* %p) +} + +void test5(read_only pipe int4 p) { +// CHECK: define void @test5(%opencl.pipe_t* %p) +} diff --git a/test/Driver/amdgpu-toolchain.c b/test/Driver/amdgpu-toolchain.c index 41cef7f4c323..c84a154c7c18 100644 --- a/test/Driver/amdgpu-toolchain.c +++ b/test/Driver/amdgpu-toolchain.c @@ -1,3 +1,3 @@ // RUN: %clang -### -target amdgcn--amdhsa -x assembler -mcpu=kaveri %s 2>&1 | FileCheck -check-prefix=AS_LINK %s // AS_LINK: clang{{.*}} "-cc1as" -// AS_LINK: lld{{.*}} "-flavor" "old-gnu" "-target" "amdgcn--amdhsa" +// AS_LINK: ld.lld{{.*}} diff --git a/test/Driver/appletvos-version-min.c b/test/Driver/appletvos-version-min.c index 9ff8297fe9eb..7cbb2001a3ec 100644 --- a/test/Driver/appletvos-version-min.c +++ b/test/Driver/appletvos-version-min.c @@ -2,6 +2,7 @@ // REQUIRES: aarch64-registered-target // RUN: %clang -target i386-apple-darwin10 -mappletvsimulator-version-min=9.0 -arch x86_64 -S -o - %s | FileCheck %s // RUN: %clang -target armv7s-apple-darwin10 -mappletvos-version-min=9.0 -arch arm64 -S -o - %s | FileCheck %s +// RUN: env TVOS_DEPLOYMENT_TARGET=9.0 %clang -isysroot SDKs/MacOSX10.9.sdk -target i386-apple-darwin10 -arch x86_64 -S -o - %s | FileCheck %s int main() { return 0; } // CHECK: .tvos_version_min 9, 0 diff --git a/test/Driver/arm-xscale.c b/test/Driver/arm-xscale.c new file mode 100644 index 000000000000..9b00b832095d --- /dev/null +++ b/test/Driver/arm-xscale.c @@ -0,0 +1,3 @@ +// RUN: %clang -target arm-freebsd -mcpu=xscale -### -c %s 2>&1 | FileCheck %s +// CHECK-NOT: error: the clang compiler does not support '-mcpu=xscale' +// CHECK: "-cc1"{{.*}} "-target-cpu" "xscale"{{.*}} diff --git a/test/Driver/cl-options.c b/test/Driver/cl-options.c index c23aefea146d..c5985a9c2b24 100644 --- a/test/Driver/cl-options.c +++ b/test/Driver/cl-options.c @@ -14,9 +14,14 @@ // C_P: "-E" // C_P: "-C" -// RUN: %clang_cl /Dfoo=bar -### -- %s 2>&1 | FileCheck -check-prefix=D %s -// RUN: %clang_cl /D foo=bar -### -- %s 2>&1 | FileCheck -check-prefix=D %s +// RUN: %clang_cl /Dfoo=bar /D bar=baz /DMYDEF#value /DMYDEF2=foo#bar /DMYDEF3#a=b /DMYDEF4# \ +// RUN: -### -- %s 2>&1 | FileCheck -check-prefix=D %s // D: "-D" "foo=bar" +// D: "-D" "bar=baz" +// D: "-D" "MYDEF=value" +// D: "-D" "MYDEF2=foo#bar" +// D: "-D" "MYDEF3=a=b" +// D: "-D" "MYDEF4=" // RUN: %clang_cl /E -### -- %s 2>&1 | FileCheck -check-prefix=E %s // E: "-E" diff --git a/test/Driver/cuda-bad-arch.cu b/test/Driver/cuda-bad-arch.cu new file mode 100644 index 000000000000..f92bdcebd0d5 --- /dev/null +++ b/test/Driver/cuda-bad-arch.cu @@ -0,0 +1,22 @@ +// Checks errors generated by passing a bad value for --cuda-gpu-arch. +// REQUIRES: clang-driver +// REQUIRES: x86-registered-target +// REQUIRES: nvptx-registered-target + +// RUN: %clang -### -target x86_64-linux-gnu --cuda-gpu-arch=compute_20 -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix BAD %s +// RUN: %clang -### -target x86_64-linux-gnu --cuda-gpu-arch=sm20 -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix BAD %s +// RUN: %clang -### -target x86_64-linux-gnu --cuda-gpu-arch=sm_19 -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix BAD %s + +// BAD: error: Unsupported CUDA gpu architecture + +// RUN: %clang -### -target x86_64-linux-gnu --cuda-gpu-arch=sm_20 -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix OK %s +// RUN: %clang -### -target x86_64-linux-gnu --cuda-gpu-arch=sm_52 -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix OK %s +// RUN: %clang -### -target x86_64-linux-gnu -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix OK %s + +// OK-NOT: error: Unsupported CUDA gpu architecture diff --git a/test/Driver/cuda-options.cu b/test/Driver/cuda-options.cu index 21625259d3ef..bf71633a4cf9 100644 --- a/test/Driver/cuda-options.cu +++ b/test/Driver/cuda-options.cu @@ -3,193 +3,140 @@ // REQUIRES: x86-registered-target // REQUIRES: nvptx-registered-target -// Simple compilation case: +// Simple compilation case. Compile device-side to PTX assembly and make sure +// we use it on the host side. // RUN: %clang -### -target x86_64-linux-gnu -c %s 2>&1 \ -// Compile device-side to PTX assembly and make sure we use it on the host side. -// RUN: | FileCheck -check-prefix CUDA-D1 -check-prefix CUDA-D1NS\ -// Then compile host side and incorporate device code. -// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-I1 \ -// Make sure we don't link anything. -// RUN: -check-prefix CUDA-NL %s - -// Typical compilation + link case: +// RUN: | FileCheck -check-prefix DEVICE -check-prefix DEVICE-NOSAVE \ +// RUN: -check-prefix HOST -check-prefix INCLUDES-DEVICE \ +// RUN: -check-prefix NOLINK %s + +// Typical compilation + link case. // RUN: %clang -### -target x86_64-linux-gnu %s 2>&1 \ -// Compile device-side to PTX assembly and make sure we use it on the host side -// RUN: | FileCheck -check-prefix CUDA-D1 -check-prefix CUDA-D1NS\ -// Then compile host side and incorporate device code. -// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-I1 \ -// Then link things. -// RUN: -check-prefix CUDA-L %s - -// Verify that --cuda-host-only disables device-side compilation and linking +// RUN: | FileCheck -check-prefix DEVICE -check-prefix DEVICE-NOSAVE \ +// RUN: -check-prefix HOST -check-prefix INCLUDES-DEVICE \ +// RUN: -check-prefix LINK %s + +// Verify that --cuda-host-only disables device-side compilation, but doesn't +// disable host-side compilation/linking. // RUN: %clang -### -target x86_64-linux-gnu --cuda-host-only %s 2>&1 \ -// Make sure we didn't run device-side compilation. -// RUN: | FileCheck -check-prefix CUDA-ND \ -// Then compile host side and make sure we don't attempt to incorporate GPU code. -// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-NI \ -// Linking is allowed to happen, even if we're missing GPU code. -// RUN: -check-prefix CUDA-L %s - -// Same test as above, but with preceeding --cuda-device-only to make -// sure only last option has effect. +// RUN: | FileCheck -check-prefix NODEVICE -check-prefix HOST \ +// RUN: -check-prefix NOINCLUDES-DEVICE -check-prefix LINK %s + +// Same test as above, but with preceeding --cuda-device-only to make sure only +// the last option has an effect. // RUN: %clang -### -target x86_64-linux-gnu --cuda-device-only --cuda-host-only %s 2>&1 \ -// Make sure we didn't run device-side compilation. -// RUN: | FileCheck -check-prefix CUDA-ND \ -// Then compile host side and make sure we don't attempt to incorporate GPU code. -// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-NI \ -// Linking is allowed to happen, even if we're missing GPU code. -// RUN: -check-prefix CUDA-L %s - -// Verify that --cuda-device-only disables host-side compilation and linking +// RUN: | FileCheck -check-prefix NODEVICE -check-prefix HOST \ +// RUN: -check-prefix NOINCLUDES-DEVICE -check-prefix LINK %s + +// Verify that --cuda-device-only disables host-side compilation and linking. // RUN: %clang -### -target x86_64-linux-gnu --cuda-device-only %s 2>&1 \ -// Compile device-side to PTX assembly -// RUN: | FileCheck -check-prefix CUDA-D1 -check-prefix CUDA-D1NS\ -// Make sure there are no host cmpilation or linking. -// RUN: -check-prefix CUDA-NH -check-prefix CUDA-NL %s +// RUN: | FileCheck -check-prefix DEVICE -check-prefix DEVICE-NOSAVE \ +// RUN: -check-prefix NOHOST -check-prefix NOLINK %s -// Same test as above, but with preceeding --cuda-host-only to make -// sure only last option has effect. +// Same test as above, but with preceeding --cuda-host-only to make sure only +// the last option has an effect. // RUN: %clang -### -target x86_64-linux-gnu --cuda-host-only --cuda-device-only %s 2>&1 \ -// Compile device-side to PTX assembly -// RUN: | FileCheck -check-prefix CUDA-D1 -check-prefix CUDA-D1NS\ -// Make sure there are no host cmpilation or linking. -// RUN: -check-prefix CUDA-NH -check-prefix CUDA-NL %s +// RUN: | FileCheck -check-prefix DEVICE -check-prefix DEVICE-NOSAVE \ +// RUN: -check-prefix NOHOST -check-prefix NOLINK %s -// Verify that with -S we compile host and device sides to assembly -// and incorporate device code on the host side. +// Verify that with -S we compile host and device sides to assembly and +// incorporate device code into the host side. // RUN: %clang -### -target x86_64-linux-gnu -S -c %s 2>&1 \ -// Compile device-side to PTX assembly -// RUN: | FileCheck -check-prefix CUDA-D1 -check-prefix CUDA-D1NS\ -// Then compile host side and incorporate GPU code. -// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-I1 \ -// Make sure we don't link anything. -// RUN: -check-prefix CUDA-NL %s - -// Verify that --cuda-gpu-arch option passes correct GPU -// archtecture info to device compilation. +// RUN: | FileCheck -check-prefix DEVICE -check-prefix DEVICE-NOSAVE \ +// RUN: -check-prefix HOST -check-prefix INCLUDES-DEVICE \ +// RUN: -check-prefix NOLINK %s + +// Verify that --cuda-gpu-arch option passes the correct GPU archtecture to +// device compilation. // RUN: %clang -### -target x86_64-linux-gnu --cuda-gpu-arch=sm_35 -c %s 2>&1 \ -// Compile device-side to PTX assembly. -// RUN: | FileCheck -check-prefix CUDA-D1 -check-prefix CUDA-D1NS \ -// RUN: -check-prefix CUDA-D1-SM35 \ -// Then compile host side and incorporate GPU code. -// RUN: -check-prefix CUDA-H -check-prefix CUDA-H-I1 \ -// Make sure we don't link anything. -// RUN: -check-prefix CUDA-NL %s - -// Verify that there is device-side compilation per --cuda-gpu-arch args +// RUN: | FileCheck -check-prefix DEVICE -check-prefix DEVICE-NOSAVE \ +// RUN: -check-prefix DEVICE-SM35 -check-prefix HOST \ +// RUN: -check-prefix INCLUDES-DEVICE -check-prefix NOLINK %s + +// Verify that there is one device-side compilation per --cuda-gpu-arch args // and that all results are included on the host side. // RUN: %clang -### -target x86_64-linux-gnu \ -// RUN: --cuda-gpu-arch=sm_35 --cuda-gpu-arch=sm_30 -c %s 2>&1 \ -// Compile both device-sides to PTX assembly -// RUN: | FileCheck \ -// RUN: -check-prefix CUDA-D1 -check-prefix CUDA-D1NS -check-prefix CUDA-D1-SM35 \ -// RUN: -check-prefix CUDA-D2 -check-prefix CUDA-D2-SM30 \ -// Then compile host side and incorporate both device-side outputs -// RUN: -check-prefix CUDA-H -check-prefix CUDA-HNS \ -// RUN: -check-prefix CUDA-H-I1 -check-prefix CUDA-H-I2 \ -// Make sure we don't link anything. -// RUN: -check-prefix CUDA-NL %s - -// Verify that device-side results are passed to correct tool when -// -save-temps is used +// RUN: --cuda-gpu-arch=sm_35 --cuda-gpu-arch=sm_30 -c %s 2>&1 \ +// RUN: | FileCheck -check-prefix DEVICE -check-prefix DEVICE-NOSAVE \ +// RUN: -check-prefix DEVICE2 -check-prefix DEVICE-SM35 \ +// RUN: -check-prefix DEVICE2-SM30 -check-prefix HOST \ +// RUN: -check-prefix HOST-NOSAVE -check-prefix INCLUDES-DEVICE \ +// RUN: -check-prefix INCLUDES-DEVICE2 -check-prefix NOLINK %s + +// Verify that device-side results are passed to the correct tool when +// -save-temps is used. // RUN: %clang -### -target x86_64-linux-gnu -save-temps -c %s 2>&1 \ -// Compile device-side to PTX assembly and make sure we use it on the host side. -// RUN: | FileCheck -check-prefix CUDA-D1 -check-prefix CUDA-D1S \ -// Then compile host side and incorporate device code. -// RUN: -check-prefix CUDA-H -check-prefix CUDA-HS -check-prefix CUDA-HS-I1 \ -// Make sure we don't link anything. -// RUN: -check-prefix CUDA-NL %s - -// Verify that device-side results are passed to correct tool when -// -fno-integrated-as is used +// RUN: | FileCheck -check-prefix DEVICE -check-prefix DEVICE-SAVE \ +// RUN: -check-prefix HOST -check-prefix HOST-SAVE -check-prefix NOLINK %s + +// Verify that device-side results are passed to the correct tool when +// -fno-integrated-as is used. // RUN: %clang -### -target x86_64-linux-gnu -fno-integrated-as -c %s 2>&1 \ -// Compile device-side to PTX assembly and make sure we use it on the host side. -// RUN: | FileCheck -check-prefix CUDA-D1 -check-prefix CUDA-D1NS \ -// Then compile host side and incorporate device code. -// RUN: -check-prefix CUDA-H -check-prefix CUDA-HNS -check-prefix CUDA-HS-I1 \ -// RUN: -check-prefix CUDA-H-AS \ -// Make sure we don't link anything. -// RUN: -check-prefix CUDA-NL %s - -// --cuda-host-only should never trigger unused arg warning. -// RUN: %clang -### -target x86_64-linux-gnu --cuda-host-only -c %s 2>&1 | \ -// RUN: FileCheck -check-prefix CUDA-NO-UNUSED-CHO %s -// RUN: %clang -### -target x86_64-linux-gnu --cuda-host-only -x c -c %s 2>&1 | \ -// RUN: FileCheck -check-prefix CUDA-NO-UNUSED-CHO %s - -// --cuda-device-only should not produce warning compiling CUDA files -// RUN: %clang -### -target x86_64-linux-gnu --cuda-device-only -c %s 2>&1 | \ -// RUN: FileCheck -check-prefix CUDA-NO-UNUSED-CDO %s - -// --cuda-device-only should warn during non-CUDA compilation. -// RUN: %clang -### -target x86_64-linux-gnu --cuda-device-only -x c -c %s 2>&1 | \ -// RUN: FileCheck -check-prefix CUDA-UNUSED-CDO %s - -// Match device-side preprocessor, and compiler phases with -save-temps -// CUDA-D1S: "-cc1" "-triple" "nvptx64-nvidia-cuda" -// CUDA-D1S-SAME: "-aux-triple" "x86_64--linux-gnu" -// CUDA-D1S-SAME: "-fcuda-is-device" -// CUDA-D1S-SAME: "-x" "cuda" - -// CUDA-D1S: "-cc1" "-triple" "nvptx64-nvidia-cuda" -// CUDA-D1S-SAME: "-aux-triple" "x86_64--linux-gnu" -// CUDA-D1S-SAME: "-fcuda-is-device" -// CUDA-D1S-SAME: "-x" "cuda-cpp-output" - -// Match the job that produces PTX assembly -// CUDA-D1: "-cc1" "-triple" "nvptx64-nvidia-cuda" -// CUDA-D1NS-SAME: "-aux-triple" "x86_64--linux-gnu" -// CUDA-D1-SAME: "-fcuda-is-device" -// CUDA-D1-SM35-SAME: "-target-cpu" "sm_35" -// CUDA-D1-SAME: "-o" "[[GPUBINARY1:[^"]*]]" -// CUDA-D1NS-SAME: "-x" "cuda" -// CUDA-D1S-SAME: "-x" "ir" - -// Match another device-side compilation -// CUDA-D2: "-cc1" "-triple" "nvptx64-nvidia-cuda" -// CUDA-D2-SAME: "-aux-triple" "x86_64--linux-gnu" -// CUDA-D2-SAME: "-fcuda-is-device" -// CUDA-D2-SM30-SAME: "-target-cpu" "sm_30" -// CUDA-D2-SAME: "-o" "[[GPUBINARY2:[^"]*]]" -// CUDA-D2-SAME: "-x" "cuda" - -// Match no device-side compilation -// CUDA-ND-NOT: "-cc1" "-triple" "nvptx64-nvidia-cuda" -// CUDA-ND-SAME-NOT: "-fcuda-is-device" - -// Match host-side preprocessor job with -save-temps -// CUDA-HS: "-cc1" "-triple" "x86_64--linux-gnu" -// CUDA-HS-SAME: "-aux-triple" "nvptx64-nvidia-cuda" -// CUDA-HS-SAME-NOT: "-fcuda-is-device" -// CUDA-HS-SAME: "-x" "cuda" - -// Match host-side compilation -// CUDA-H: "-cc1" "-triple" "x86_64--linux-gnu" -// CUDA-H-SAME: "-aux-triple" "nvptx64-nvidia-cuda" -// CUDA-H-SAME-NOT: "-fcuda-is-device" -// CUDA-H-SAME: "-o" "[[HOSTOUTPUT:[^"]*]]" -// CUDA-HNS-SAME: "-x" "cuda" -// CUDA-HS-SAME: "-x" "cuda-cpp-output" -// CUDA-H-I1-SAME: "-fcuda-include-gpubinary" "[[GPUBINARY1]]" -// CUDA-H-I2-SAME: "-fcuda-include-gpubinary" "[[GPUBINARY2]]" - -// Match external assembler that uses compilation output -// CUDA-H-AS: "-o" "{{.*}}.o" "[[HOSTOUTPUT]]" +// RUN: | FileCheck -check-prefix DEVICE -check-prefix DEVICE-NOSAVE \ +// RUN: -check-prefix HOST -check-prefix HOST-NOSAVE \ +// RUN: -check-prefix HOST-AS -check-prefix NOLINK %s + +// Match device-side preprocessor and compiler phases with -save-temps. +// DEVICE-SAVE: "-cc1" "-triple" "nvptx64-nvidia-cuda" +// DEVICE-SAVE-SAME: "-aux-triple" "x86_64--linux-gnu" +// DEVICE-SAVE-SAME: "-fcuda-is-device" +// DEVICE-SAVE-SAME: "-x" "cuda" + +// DEVICE-SAVE: "-cc1" "-triple" "nvptx64-nvidia-cuda" +// DEVICE-SAVE-SAME: "-aux-triple" "x86_64--linux-gnu" +// DEVICE-SAVE-SAME: "-fcuda-is-device" +// DEVICE-SAVE-SAME: "-x" "cuda-cpp-output" + +// Match the job that produces PTX assembly. +// DEVICE: "-cc1" "-triple" "nvptx64-nvidia-cuda" +// DEVICE-NOSAVE-SAME: "-aux-triple" "x86_64--linux-gnu" +// DEVICE-SAME: "-fcuda-is-device" +// DEVICE-SM35-SAME: "-target-cpu" "sm_35" +// DEVICE-SAME: "-o" "[[GPUBINARY1:[^"]*]]" +// DEVICE-NOSAVE-SAME: "-x" "cuda" +// DEVICE-SAVE-SAME: "-x" "ir" + +// Match another device-side compilation. +// DEVICE2: "-cc1" "-triple" "nvptx64-nvidia-cuda" +// DEVICE2-SAME: "-aux-triple" "x86_64--linux-gnu" +// DEVICE2-SAME: "-fcuda-is-device" +// DEVICE2-SM30-SAME: "-target-cpu" "sm_30" +// DEVICE2-SAME: "-o" "[[GPUBINARY2:[^"]*]]" +// DEVICE2-SAME: "-x" "cuda" + +// Match no device-side compilation. +// NODEVICE-NOT: "-cc1" "-triple" "nvptx64-nvidia-cuda" +// NODEVICE-SAME-NOT: "-fcuda-is-device" + +// Match host-side preprocessor job with -save-temps. +// HOST-SAVE: "-cc1" "-triple" "x86_64--linux-gnu" +// HOST-SAVE-SAME: "-aux-triple" "nvptx64-nvidia-cuda" +// HOST-SAVE-SAME-NOT: "-fcuda-is-device" +// HOST-SAVE-SAME: "-x" "cuda" + +// Match host-side compilation. +// HOST: "-cc1" "-triple" "x86_64--linux-gnu" +// HOST-SAME: "-aux-triple" "nvptx64-nvidia-cuda" +// HOST-SAME-NOT: "-fcuda-is-device" +// HOST-SAME: "-o" "[[HOSTOUTPUT:[^"]*]]" +// HOST-NOSAVE-SAME: "-x" "cuda" +// HOST-SAVE-SAME: "-x" "cuda-cpp-output" +// INCLUDES-DEVICE-SAME: "-fcuda-include-gpubinary" "[[GPUBINARY1]]" +// INCLUDES-DEVICE2-SAME: "-fcuda-include-gpubinary" "[[GPUBINARY2]]" + +// Match external assembler that uses compilation output. +// HOST-AS: "-o" "{{.*}}.o" "[[HOSTOUTPUT]]" // Match no GPU code inclusion. -// CUDA-H-NI-NOT: "-fcuda-include-gpubinary" - -// Match no CUDA compilation -// CUDA-NH-NOT: "-cc1" "-triple" -// CUDA-NH-SAME-NOT: "-x" "cuda" +// NOINCLUDES-DEVICE-NOT: "-fcuda-include-gpubinary" -// Match linker -// CUDA-L: "{{.*}}{{ld|link}}{{(.exe)?}}" -// CUDA-L-SAME: "[[HOSTOUTPUT]]" +// Match no host compilation. +// NOHOST-NOT: "-cc1" "-triple" +// NOHOST-SAME-NOT: "-x" "cuda" -// Match no linker -// CUDA-NL-NOT: "{{.*}}{{ld|link}}{{(.exe)?}}" +// Match linker. +// LINK: "{{.*}}{{ld|link}}{{(.exe)?}}" +// LINK-SAME: "[[HOSTOUTPUT]]" -// CUDA-NO-UNUSED-CHO-NOT: warning: argument unused during compilation: '--cuda-host-only' -// CUDA-UNUSED-CDO: warning: argument unused during compilation: '--cuda-device-only' -// CUDA-NO-UNUSED-CDO-NOT: warning: argument unused during compilation: '--cuda-device-only' +// Match no linker. +// NOLINK-NOT: "{{.*}}{{ld|link}}{{(.exe)?}}" diff --git a/test/Driver/cuda-unused-arg-warning.cu b/test/Driver/cuda-unused-arg-warning.cu new file mode 100644 index 000000000000..e8daad6cdaf6 --- /dev/null +++ b/test/Driver/cuda-unused-arg-warning.cu @@ -0,0 +1,23 @@ +// Tests that we trigger unused-arg warnings on CUDA flags appropriately. + +// REQUIRES: clang-driver +// REQUIRES: x86-registered-target +// REQUIRES: nvptx-registered-target + +// --cuda-host-only should never trigger unused arg warning. +// RUN: %clang -### -target x86_64-linux-gnu --cuda-host-only -c %s 2>&1 | \ +// RUN: FileCheck %s +// RUN: %clang -### -target x86_64-linux-gnu --cuda-host-only -x c -c %s 2>&1 | \ +// RUN: FileCheck %s + +// --cuda-device-only should warn during non-CUDA compilation. +// RUN: %clang -### -target x86_64-linux-gnu --cuda-device-only -x c -c %s 2>&1 | \ +// RUN: FileCheck -check-prefix UNUSED-WARNING %s + +// --cuda-device-only should not produce warning compiling CUDA files +// RUN: %clang -### -target x86_64-linux-gnu --cuda-device-only -c %s 2>&1 | \ +// RUN: FileCheck -check-prefix NO-UNUSED-WARNING %s + +// CHECK-NOT: warning: argument unused during compilation: '--cuda-host-only' +// UNUSED-WARNING: warning: argument unused during compilation: '--cuda-device-only' +// NO-UNUSED-WARNING-NOT: warning: argument unused during compilation: '--cuda-device-only' diff --git a/test/Driver/debug-options.c b/test/Driver/debug-options.c index 72d0136a8c40..0ccacd0fc6c7 100644 --- a/test/Driver/debug-options.c +++ b/test/Driver/debug-options.c @@ -32,7 +32,7 @@ // RUN: | FileCheck -check-prefix=G_DARWIN %s // RUN: %clang -### -c -g %s -target x86_64-pc-freebsd10.0 2>&1 \ -// RUN: | FileCheck -check-prefix=G_LLDB %s +// RUN: | FileCheck -check-prefix=G_GDB %s // On the PS4, -g defaults to -gno-column-info, and we always generate the // arange section. diff --git a/test/Driver/fortran.f95 b/test/Driver/fortran.f95 index 982f4eb5e6df..47c6e7b50c95 100644 --- a/test/Driver/fortran.f95 +++ b/test/Driver/fortran.f95 @@ -13,3 +13,9 @@ // CHECK-ASM: "-S" // CHECK-ASM: "-x" "f95" // CHECK-ASM-NOT: cc1 + +// RUN: %clang -Wall -target x86_64-unknown-linux-gnu -integrated-as %s -o %t -### 2>&1 | FileCheck --check-prefix=CHECK-WARN %s +// CHECK-WARN: gcc +// CHECK-WARN-NOT: "-Wall" +// CHECK-WARN: ld +// CHECK-WARN-NOT: "-Wall" diff --git a/test/Driver/gcc_forward.c b/test/Driver/gcc_forward.c index 4892bd92ac69..d28e432b1e17 100644 --- a/test/Driver/gcc_forward.c +++ b/test/Driver/gcc_forward.c @@ -5,15 +5,16 @@ // RUN: %s \ // RUN: -Wall -Wdocumentation \ // RUN: -Xclang foo-bar \ -// RUN: -march=x86_64 \ +// RUN: -march=x86-64 \ // RUN: -mlinker-version=10 -### 2> %t // RUN: FileCheck < %t %s // -// clang-cc1 +// clang -cc1 +// CHECK: clang // CHECK: "-Wall" "-Wdocumentation" // CHECK: "-o" "{{[^"]+}}.o" // -// gcc-ld +// gcc as ld. // CHECK: gcc{{[^"]*}}" // CHECK-NOT: "-mlinker-version=10" // CHECK-NOT: "-Xclang" @@ -27,3 +28,9 @@ // CHECK-NOT: "-Wall" // CHECK-NOT: "-Wdocumentation" // CHECK: "-o" "a.out" + +// Check that we're not forwarding -g options to the assembler +// RUN: %clang -g -target x86_64-unknown-linux-gnu -no-integrated-as -c %s -### 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-ASM %s +// CHECK-ASM: as +// CHECK-ASM-NOT: "-g" diff --git a/test/Driver/pic.c b/test/Driver/pic.c index 06b4204e60da..aeb2ee33114c 100644 --- a/test/Driver/pic.c +++ b/test/Driver/pic.c @@ -218,6 +218,8 @@ // RUN: | FileCheck %s --check-prefix=CHECK-NO-PIC // RUN: %clang -c %s -target armv7-apple-ios -fapple-kext -miphoneos-version-min=6.0.0 -static -### 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECK-NO-PIC +// RUN: %clang -c %s -target armv7-apple-unknown-macho -static -### 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-NO-PIC // // On OpenBSD, PIE is enabled by default, but can be disabled. // RUN: %clang -c %s -target amd64-pc-openbsd -### 2>&1 \ diff --git a/test/Driver/wasm-toolchain.c b/test/Driver/wasm-toolchain.c index 4d707a0f6c8f..b9685b160192 100644 --- a/test/Driver/wasm-toolchain.c +++ b/test/Driver/wasm-toolchain.c @@ -1,3 +1,44 @@ -// RUN: %clang -### -no-canonical-prefixes -target wasm32-unknown-unknown -x assembler %s 2>&1 | FileCheck -check-prefix=AS_LINK %s -// AS_LINK: clang{{.*}}" "-cc1as" {{.*}} "-o" "[[temp:[^"]*]]" -// AS_LINK: lld{{.*}}" "-flavor" "ld" "[[temp]]" "-o" "a.out" +// A basic clang -cc1 command-line. WebAssembly is somewhat special in +// enabling -ffunction-sections, -fdata-sections, and -fvisibility=hidden by +// default. + +// RUN: %clang %s -### -no-canonical-prefixes -target wasm32-unknown-unknown 2>&1 | FileCheck -check-prefix=CC1 %s +// CC1: clang{{.*}} "-cc1" "-triple" "wasm32-unknown-unknown" {{.*}} "-fvisibility" "hidden" {{.*}} "-ffunction-sections" "-fdata-sections" + +// Ditto, but ensure that a user -fno-function-sections disables the +// default -ffunction-sections. + +// RUN: %clang %s -### -target wasm32-unknown-unknown -fno-function-sections 2>&1 | FileCheck -check-prefix=NO_FUNCTION_SECTIONS %s +// NO_FUNCTION_SECTIONS-NOT: function-sections + +// Ditto, but ensure that a user -fno-data-sections disables the +// default -fdata-sections. + +// RUN: %clang %s -### -target wasm32-unknown-unknown -fno-data-sections 2>&1 | FileCheck -check-prefix=NO_DATA_SECTIONS %s +// NO_DATA_SECTIONS-NOT: data-sections + +// Ditto, but ensure that a user -fvisibility=default disables the default +// -fvisibility=hidden. + +// RUN: %clang %s -### -target wasm32-unknown-unknown -fvisibility=default 2>&1 | FileCheck -check-prefix=FVISIBILITY_DEFAULT %s +// FVISIBILITY_DEFAULT-NOT: hidden + +// A basic C link command-line. + +// RUN: %clang -### -no-canonical-prefixes -target wasm32-unknown-unknown %s 2>&1 | FileCheck -check-prefix=LINK %s +// LINK: clang{{.*}}" "-cc1" {{.*}} "-o" "[[temp:[^"]*]]" +// LINK: lld{{.*}}" "-flavor" "ld" "[[temp]]" "-o" "a.out" + +// A basic C link command-line with optimization. WebAssembly is somewhat +// special in enabling --gc-sections by default. + +// RUN: %clang -### -O2 -no-canonical-prefixes -target wasm32-unknown-unknown %s 2>&1 | FileCheck -check-prefix=LINK_OPT %s +// LINK_OPT: clang{{.*}}" "-cc1" {{.*}} "-o" "[[temp:[^"]*]]" +// LINK_OPT: lld{{.*}}" "-flavor" "ld" "--gc-sections" "[[temp]]" "-o" "a.out" + +// Ditto, but ensure that a user --no-gc-sections comes after the +// default --gc-sections. + +// RUN: %clang -### -O2 -no-canonical-prefixes -target wasm32-unknown-unknown -Wl,--no-gc-sections %s 2>&1 | FileCheck -check-prefix=NO_GC_SECTIONS %s +// NO_GC_SECTIONS: clang{{.*}}" "-cc1" {{.*}} "-o" "[[temp:[^"]*]]" +// NO_GC_SECTIONS: lld{{.*}}" "-flavor" "ld" "--gc-sections" "--no-gc-sections" "[[temp]]" "-o" "a.out" diff --git a/test/Driver/wasm32-unknown-unknown.cpp b/test/Driver/wasm32-unknown-unknown.cpp index c47428796dc9..3f44d93df570 100644 --- a/test/Driver/wasm32-unknown-unknown.cpp +++ b/test/Driver/wasm32-unknown-unknown.cpp @@ -13,34 +13,34 @@ typedef __PTRDIFF_TYPE__ ptrdiff_t; extern "C" { -// CHECK: @align_c = global i32 1 +// CHECK: @align_c = hidden global i32 1 int align_c = __alignof(char); -// CHECK: @align_s = global i32 2 +// CHECK: @align_s = hidden global i32 2 int align_s = __alignof(short); -// CHECK: @align_i = global i32 4 +// CHECK: @align_i = hidden global i32 4 int align_i = __alignof(int); -// CHECK: @align_l = global i32 4 +// CHECK: @align_l = hidden global i32 4 int align_l = __alignof(long); -// CHECK: @align_ll = global i32 8 +// CHECK: @align_ll = hidden global i32 8 int align_ll = __alignof(long long); -// CHECK: @align_p = global i32 4 +// CHECK: @align_p = hidden global i32 4 int align_p = __alignof(void*); -// CHECK: @align_f = global i32 4 +// CHECK: @align_f = hidden global i32 4 int align_f = __alignof(float); -// CHECK: @align_d = global i32 8 +// CHECK: @align_d = hidden global i32 8 int align_d = __alignof(double); -// CHECK: @align_ld = global i32 16 +// CHECK: @align_ld = hidden global i32 16 int align_ld = __alignof(long double); -// CHECK: @align_vl = global i32 4 +// CHECK: @align_vl = hidden global i32 4 int align_vl = __alignof(va_list); // CHECK: _GNU_SOURCEdefined diff --git a/test/Driver/wasm64-unknown-unknown.cpp b/test/Driver/wasm64-unknown-unknown.cpp index c33f4e56a65f..6162759884a8 100644 --- a/test/Driver/wasm64-unknown-unknown.cpp +++ b/test/Driver/wasm64-unknown-unknown.cpp @@ -13,34 +13,34 @@ typedef __PTRDIFF_TYPE__ ptrdiff_t; extern "C" { -// CHECK: @align_c = global i32 1 +// CHECK: @align_c = hidden global i32 1 int align_c = __alignof(char); -// CHECK: @align_s = global i32 2 +// CHECK: @align_s = hidden global i32 2 int align_s = __alignof(short); -// CHECK: @align_i = global i32 4 +// CHECK: @align_i = hidden global i32 4 int align_i = __alignof(int); -// CHECK: @align_l = global i32 8 +// CHECK: @align_l = hidden global i32 8 int align_l = __alignof(long); -// CHECK: @align_ll = global i32 8 +// CHECK: @align_ll = hidden global i32 8 int align_ll = __alignof(long long); -// CHECK: @align_p = global i32 8 +// CHECK: @align_p = hidden global i32 8 int align_p = __alignof(void*); -// CHECK: @align_f = global i32 4 +// CHECK: @align_f = hidden global i32 4 int align_f = __alignof(float); -// CHECK: @align_d = global i32 8 +// CHECK: @align_d = hidden global i32 8 int align_d = __alignof(double); -// CHECK: @align_ld = global i32 16 +// CHECK: @align_ld = hidden global i32 16 int align_ld = __alignof(long double); -// CHECK: @align_vl = global i32 8 +// CHECK: @align_vl = hidden global i32 8 int align_vl = __alignof(va_list); // CHECK: _GNU_SOURCEdefined diff --git a/test/Index/print-type-declaration.cpp b/test/Index/print-type-declaration.cpp new file mode 100644 index 000000000000..31c0a73fcd06 --- /dev/null +++ b/test/Index/print-type-declaration.cpp @@ -0,0 +1,12 @@ + +class Test{}; + +int main() +{ + auto a = Test(); + auto b = a; +} + +// RUN: c-index-test -test-print-type-declaration -std=c++11 %s | FileCheck %s +// CHECK: VarDecl=a:6:8 (Definition) [typedeclaration=Test] [typekind=Record] +// CHECK: VarDecl=b:7:8 (Definition) [typedeclaration=Test] [typekind=Record] diff --git a/test/Misc/ast-dump-lookups.cpp b/test/Misc/ast-dump-lookups.cpp index 5c6da48b3afb..2d235010cb73 100644 --- a/test/Misc/ast-dump-lookups.cpp +++ b/test/Misc/ast-dump-lookups.cpp @@ -1,16 +1,31 @@ // RUN: %clang_cc1 -std=c++11 -ast-dump -ast-dump-filter Test %s | FileCheck -check-prefix DECLS %s // RUN: %clang_cc1 -std=c++11 -ast-dump-lookups -ast-dump-filter Test %s | FileCheck -check-prefix LOOKUPS %s // RUN: %clang_cc1 -std=c++11 -ast-dump -ast-dump-lookups -ast-dump-filter Test %s | FileCheck -check-prefix DECLS-LOOKUPS %s +// RUN: %clang_cc1 -std=c++11 -DPRAGMA -fsyntax-only %s 2>&1 | FileCheck -check-prefix PRAGMA %s namespace Test { + typedef int T; extern int a; int a = 0; } +#ifdef PRAGMA +#pragma clang __debug dump Test +// PRAGMA: lookup results for Test: +// PRAGMA-NEXT: NamespaceDecl {{.*}} Test +// PRAGMA-NEXT: |-TypedefDecl {{.*}} T 'int' +// PRAGMA-NEXT: | `-BuiltinType {{.*}} 'int' +// PRAGMA-NEXT: |-VarDecl [[EXTERN_A:0x[^ ]*]] {{.*}} a 'int' extern +// PRAGMA-NEXT: `-VarDecl {{.*}} prev [[EXTERN_A]] {{.*}} a 'int' cinit +// PRAGMA-NEXT: `-IntegerLiteral {{.*}} 'int' 0 +#endif + namespace Test { } // DECLS: Dumping Test: // DECLS-NEXT: NamespaceDecl {{.*}} Test +// DECLS-NEXT: |-TypedefDecl {{.*}} T 'int' +// DECLS-NEXT: | `-BuiltinType {{.*}} 'int' // DECLS-NEXT: |-VarDecl [[EXTERN_A:0x[^ ]*]] {{.*}} a 'int' extern // DECLS-NEXT: `-VarDecl {{.*}} prev [[EXTERN_A]] {{.*}} a 'int' cinit // DECLS-NEXT: `-IntegerLiteral {{.*}} 'int' 0 @@ -20,15 +35,15 @@ namespace Test { } // LOOKUPS: Dumping Test: // LOOKUPS-NEXT: StoredDeclsMap Namespace {{.*}} 'Test' -// LOOKUPS-NEXT: `-DeclarationName 'a' -// LOOKUPS-NEXT: `-Var {{.*}} 'a' 'int' +// LOOKUPS: DeclarationName 'a' +// LOOKUPS-NEXT: `-Var {{.*}} 'a' 'int' // // LOOKUPS: Dumping Test: // LOOKUPS-NEXT: Lookup map is in primary DeclContext // DECLS-LOOKUPS: Dumping Test: // DECLS-LOOKUPS-NEXT: StoredDeclsMap Namespace {{.*}} 'Test' -// DECLS-LOOKUPS-NEXT: `-DeclarationName 'a' +// DECLS-LOOKUPS: -DeclarationName 'a' // DECLS-LOOKUPS-NEXT: `-Var [[A:[^ ]*]] 'a' 'int' // DECLS-LOOKUPS-NEXT: |-VarDecl [[EXTERN_A:0x[^ ]*]] {{.*}} a 'int' extern // DECLS-LOOKUPS-NEXT: `-VarDecl [[A]] prev [[EXTERN_A]] {{.*}} a 'int' cinit diff --git a/test/Misc/ast-print-char-literal.cpp b/test/Misc/ast-print-char-literal.cpp new file mode 100644 index 000000000000..bb5daa2444da --- /dev/null +++ b/test/Misc/ast-print-char-literal.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -ast-print -std=c++1z %s -o - | FileCheck %s + +char c = u8'1'; +char d = '1'; +char e = U'1'; +char f = L'1'; +char g = u'1'; + +template <char c = u8'1'> +void h(); + +void i() { + h<u8'2'>(); +} + +// CHECK: char c = u8'1'; +// CHECK-NEXT: char d = '1'; +// CHECK-NEXT: char e = U'1'; +// CHECK-NEXT: char f = L'1'; +// CHECK-NEXT: char g = u'1'; + +// CHECK: template <char c = u8'1'> + +// CHECK: h<u8'2'>(); diff --git a/test/Modules/Inputs/DebugCXX.h b/test/Modules/Inputs/DebugCXX.h index 6ef4445cb971..b6a52579fc3a 100644 --- a/test/Modules/Inputs/DebugCXX.h +++ b/test/Modules/Inputs/DebugCXX.h @@ -17,7 +17,7 @@ namespace DebugCXX { e2 = '2' }; - // Templates (instatiations). + // Templates (instantiations). template<typename T> struct traits {}; template<typename T, typename Traits = traits<T> @@ -50,3 +50,11 @@ namespace DebugCXX { typedef A<void> B; void foo(B) {} } + +// Virtual class with a forward declaration. +class FwdVirtual; +class FwdVirtual { + virtual ~FwdVirtual() {} +}; + +struct PureForwardDecl; diff --git a/test/Modules/Inputs/dummy.h b/test/Modules/Inputs/dummy.h index 6e1ac74e44fb..cad83154dc2e 100644 --- a/test/Modules/Inputs/dummy.h +++ b/test/Modules/Inputs/dummy.h @@ -1,3 +1,5 @@ // This module only exists to make local decl IDs and global decl IDs different. - +#ifndef DUMMY_H +#define DUMMY_H struct Dummy {} extern *dummy1, *dummy2, *dummy3; +#endif diff --git a/test/Modules/Inputs/using-decl-redecl/a.h b/test/Modules/Inputs/using-decl-redecl/a.h index 477546945c09..eaa1876aac68 100644 --- a/test/Modules/Inputs/using-decl-redecl/a.h +++ b/test/Modules/Inputs/using-decl-redecl/a.h @@ -1,2 +1,3 @@ struct string {}; -namespace N { typedef ::string clstring; } +const int n = 0; +namespace N { typedef ::string clstring; using ::n; } diff --git a/test/Modules/Inputs/using-decl-redecl/d.h b/test/Modules/Inputs/using-decl-redecl/d.h new file mode 100644 index 000000000000..2243de1baf9a --- /dev/null +++ b/test/Modules/Inputs/using-decl-redecl/d.h @@ -0,0 +1 @@ +#include "a.h" diff --git a/test/Modules/Inputs/using-decl-redecl/module.modulemap b/test/Modules/Inputs/using-decl-redecl/module.modulemap index bd6ea830c2d4..a2ebc1767645 100644 --- a/test/Modules/Inputs/using-decl-redecl/module.modulemap +++ b/test/Modules/Inputs/using-decl-redecl/module.modulemap @@ -1,3 +1,4 @@ module a { header "a.h" } -module b { header "b.h" export * } -module c { header "c.h" export * } +module b { header "b.h" export a } +module c { header "c.h" export a export b } +module d { header "d.h" } diff --git a/test/Modules/ModuleDebugInfo.cpp b/test/Modules/ModuleDebugInfo.cpp index 82500f0d162d..bbe36cb225d0 100644 --- a/test/Modules/ModuleDebugInfo.cpp +++ b/test/Modules/ModuleDebugInfo.cpp @@ -23,22 +23,34 @@ // CHECK-SAME: isOptimized: false, // CHECK-SAME-NOT: splitDebugFilename: // CHECK-DWO: dwoId: + // CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum" // CHECK-SAME: identifier: "_ZTSN8DebugCXX4EnumE") // CHECK: !DINamespace(name: "DebugCXX" + // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Struct" // CHECK-SAME: identifier: "_ZTSN8DebugCXX6StructE") + // CHECK: !DICompositeType(tag: DW_TAG_class_type, // CHECK-SAME: name: "Template<int, DebugCXX::traits<int> >" // CHECK-SAME: identifier: "_ZTSN8DebugCXX8TemplateIiNS_6traitsIiEEEE") + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "A<void>" +// CHECK-SAME: identifier: "_ZTSN8DebugCXX1AIJvEEE") + // CHECK: !DICompositeType(tag: DW_TAG_class_type, // CHECK-SAME: name: "Template<float, DebugCXX::traits<float> >" // CHECK-SAME: identifier: "_ZTSN8DebugCXX8TemplateIfNS_6traitsIfEEEE") -// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "A<void>" -// CHECK-SAME: identifier: "_ZTSN8DebugCXX1AIJvEEE") + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "FwdVirtual" +// CHECK-SAME: elements: +// CHECK-SAME: identifier: "_ZTS10FwdVirtual") +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "_vptr$FwdVirtual" + // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "FloatInstatiation" // no mangled name here yet. + // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "B", // no mangled name here yet. -// CHECK-NEG-NOT: "_ZTSN8DebugCXX8TemplateIlNS_6traitsIlEEEE" +// CHECK-NEG-NOT: !DICompositeType(tag: DW_TAG_structure_type, name: "PureForwardDecl" diff --git a/test/Modules/modular_maps.cpp b/test/Modules/modular_maps.cpp index 3b6afc7552d6..fc44131d4587 100644 --- a/test/Modules/modular_maps.cpp +++ b/test/Modules/modular_maps.cpp @@ -16,4 +16,4 @@ #include "b.h" // expected-error {{private header}} @import C; const int v = a + c + x; -const int val = a + b + c + x; // expected-error {{undeclared identifier}} +const int val = a + b + c + x; diff --git a/test/Modules/separate_map_tree.cpp b/test/Modules/separate_map_tree.cpp index 5a1fff4efc70..a5cb9888a600 100644 --- a/test/Modules/separate_map_tree.cpp +++ b/test/Modules/separate_map_tree.cpp @@ -5,4 +5,4 @@ #include "public-in-b.h" // expected-error {{private header}} #include "public-in-c.h" #include "private-in-c.h" // expected-error {{private header}} -const int val = common + b + c + c_; // expected-error {{undeclared identifier}} +const int val = common + b + c + c_; diff --git a/test/Modules/tag-injection.c b/test/Modules/tag-injection.c new file mode 100644 index 000000000000..5bb15477e2e2 --- /dev/null +++ b/test/Modules/tag-injection.c @@ -0,0 +1,18 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: echo 'struct a;' > %t/a.h +// RUN: echo 'struct b {}; void foo(struct b*);' > %t/b.h +// RUN: echo 'module X { module a { header "a.h" } module b { header "b.h" } }' > %t/x.modulemap +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%t/x.modulemap %s -I%t -verify + +#include "a.h" + +void f(struct a *p); + +// FIXME: We should warn that 'b' will not be visible outside of this function, +// but we merge this 'b' with X.b's 'b' because we don't yet implement C's +// "compatible types" rule. +void g(struct b *p); + +struct b b; // expected-error {{definition of 'b' must be imported from module 'X.b' before it is required}} +// expected-note@b.h:1 {{here}} diff --git a/test/Modules/tag-injection.cpp b/test/Modules/tag-injection.cpp index 75c8b5fecdb4..e55598b06202 100644 --- a/test/Modules/tag-injection.cpp +++ b/test/Modules/tag-injection.cpp @@ -1,12 +1,15 @@ // RUN: rm -rf %t // RUN: mkdir %t -// RUN: touch %t/a.h -// RUN: echo 'struct X {};' > %t/b.h +// RUN: echo 'struct tm;' > %t/a.h +// RUN: echo 'struct X {}; void foo(struct tm*);' > %t/b.h // RUN: echo 'module X { module a { header "a.h" } module b { header "b.h" } }' > %t/x.modulemap +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x c++ -fmodule-map-file=%t/x.modulemap %s -I%t -verify -std=c++11 // RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x c++ -fmodule-map-file=%t/x.modulemap %s -I%t -verify -fmodules-local-submodule-visibility -std=c++11 #include "a.h" +using ::tm; + struct A { // This use of 'struct X' makes the declaration (but not definition) of X visible. virtual void f(struct X *p); diff --git a/test/Modules/using-decl-redecl.cpp b/test/Modules/using-decl-redecl.cpp index 0e78cec1188f..0524052fce5b 100644 --- a/test/Modules/using-decl-redecl.cpp +++ b/test/Modules/using-decl-redecl.cpp @@ -2,10 +2,20 @@ // RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t \ // RUN: -fmodule-map-file=%S/Inputs/using-decl-redecl/module.modulemap \ // RUN: -I%S/Inputs/using-decl-redecl \ +// RUN: -Wno-modules-ambiguous-internal-linkage \ // RUN: -verify %s + +#include "d.h" + +const int n = 0; +namespace M { using ::n; } + #include "c.h" + N::clstring y = b; // Use a typo to trigger import of all declarations in N. N::clstrinh s; // expected-error {{did you mean 'clstring'}} -// expected-note@a.h:2 {{here}} +// expected-note@a.h:3 {{here}} + +namespace M { using N::n; } diff --git a/test/OpenMP/barrier_ast_print.cpp b/test/OpenMP/barrier_ast_print.cpp index fbb478bfb6b7..062df80a226e 100644 --- a/test/OpenMP/barrier_ast_print.cpp +++ b/test/OpenMP/barrier_ast_print.cpp @@ -12,6 +12,15 @@ template <class T> T tmain(T argc) { static T a; #pragma omp barrier + switch (argc) { + case 0: +#pragma omp barrier + break; + default: +#pragma omp barrier +#pragma omp barrier + break; + } return a + argc; } // CHECK: static int a; @@ -20,12 +29,39 @@ T tmain(T argc) { // CHECK-NEXT: #pragma omp barrier // CHECK: static T a; // CHECK-NEXT: #pragma omp barrier +// CHECK-NEXT: switch (argc) { +// CHECK-NEXT: case 0: +// CHECK-NEXT: #pragma omp barrier +// CHECK-NEXT: break; +// CHECK-NEXT: default: +// CHECK-NEXT: #pragma omp barrier +// CHECK-NEXT: #pragma omp barrier +// CHECK-NEXT: break; +// CHECK-NEXT: } int main(int argc, char **argv) { static int a; // CHECK: static int a; #pragma omp barrier // CHECK-NEXT: #pragma omp barrier + switch (argc) { + case 0: +#pragma omp barrier +#pragma omp barrier + break; + default: +#pragma omp barrier + break; + } +// CHECK-NEXT: switch (argc) { +// CHECK-NEXT: case 0: +// CHECK-NEXT: #pragma omp barrier +// CHECK-NEXT: #pragma omp barrier +// CHECK-NEXT: break; +// CHECK-NEXT: default: +// CHECK-NEXT: #pragma omp barrier +// CHECK-NEXT: break; +// CHECK-NEXT: } return tmain(argc) + tmain(argv[0][0]) + a; } diff --git a/test/OpenMP/barrier_messages.cpp b/test/OpenMP/barrier_messages.cpp index 4dc6480a57e2..7d79445d2857 100644 --- a/test/OpenMP/barrier_messages.cpp +++ b/test/OpenMP/barrier_messages.cpp @@ -27,7 +27,7 @@ T tmain(T argc) { #pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}} switch (argc) case 1: -#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}} +#pragma omp barrier switch (argc) case 1: { #pragma omp barrier @@ -35,7 +35,7 @@ T tmain(T argc) { switch (argc) { #pragma omp barrier case 1: -#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}} +#pragma omp barrier break; default: { #pragma omp barrier @@ -81,7 +81,7 @@ int main(int argc, char **argv) { #pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}} switch (argc) case 1: -#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}} +#pragma omp barrier switch (argc) case 1: { #pragma omp barrier @@ -89,7 +89,7 @@ int main(int argc, char **argv) { switch (argc) { #pragma omp barrier case 1: -#pragma omp barrier // expected-error {{'#pragma omp barrier' cannot be an immediate substatement}} +#pragma omp barrier break; default: { #pragma omp barrier diff --git a/test/OpenMP/cancel_messages.cpp b/test/OpenMP/cancel_messages.cpp index 07088387cb4f..e23b5c337bc8 100644 --- a/test/OpenMP/cancel_messages.cpp +++ b/test/OpenMP/cancel_messages.cpp @@ -53,7 +53,7 @@ int main(int argc, char **argv) { #pragma omp cancel parallel // expected-error {{'#pragma omp cancel' cannot be an immediate substatement}} switch (argc) case 1: -#pragma omp cancel sections // expected-error {{'#pragma omp cancel' cannot be an immediate substatement}} +#pragma omp cancel sections switch (argc) case 1: { #pragma omp cancel for @@ -61,7 +61,7 @@ int main(int argc, char **argv) { switch (argc) { #pragma omp cancel taskgroup case 1: -#pragma omp cancel parallel // expected-error {{'#pragma omp cancel' cannot be an immediate substatement}} +#pragma omp cancel parallel break; default: { #pragma omp cancel sections diff --git a/test/OpenMP/cancellation_point_messages.cpp b/test/OpenMP/cancellation_point_messages.cpp index d25cb6113d16..2324915e83f8 100644 --- a/test/OpenMP/cancellation_point_messages.cpp +++ b/test/OpenMP/cancellation_point_messages.cpp @@ -53,7 +53,7 @@ int main(int argc, char **argv) { #pragma omp cancellation point parallel // expected-error {{'#pragma omp cancellation point' cannot be an immediate substatement}} switch (argc) case 1: -#pragma omp cancellation point sections // expected-error {{'#pragma omp cancellation point' cannot be an immediate substatement}} +#pragma omp cancellation point sections switch (argc) case 1: { #pragma omp cancellation point for @@ -61,7 +61,7 @@ int main(int argc, char **argv) { switch (argc) { #pragma omp cancellation point taskgroup case 1: -#pragma omp cancellation point parallel // expected-error {{'#pragma omp cancellation point' cannot be an immediate substatement}} +#pragma omp cancellation point parallel break; default: { #pragma omp cancellation point sections diff --git a/test/OpenMP/flush_messages.cpp b/test/OpenMP/flush_messages.cpp index 2f87a2938476..1c086a3f3fa4 100644 --- a/test/OpenMP/flush_messages.cpp +++ b/test/OpenMP/flush_messages.cpp @@ -31,7 +31,7 @@ T tmain(T argc) { #pragma omp flush // expected-error {{'#pragma omp flush' cannot be an immediate substatement}} switch (argc) case 1: -#pragma omp flush // expected-error {{'#pragma omp flush' cannot be an immediate substatement}} +#pragma omp flush switch (argc) case 1: { #pragma omp flush @@ -39,7 +39,7 @@ T tmain(T argc) { switch (argc) { #pragma omp flush case 1: -#pragma omp flush // expected-error {{'#pragma omp flush' cannot be an immediate substatement}} +#pragma omp flush break; default: { #pragma omp flush @@ -95,7 +95,7 @@ int main(int argc, char **argv) { #pragma omp flush // expected-error {{'#pragma omp flush' cannot be an immediate substatement}} switch (argc) case 1: -#pragma omp flush // expected-error {{'#pragma omp flush' cannot be an immediate substatement}} +#pragma omp flush switch (argc) case 1: { #pragma omp flush @@ -103,7 +103,7 @@ int main(int argc, char **argv) { switch (argc) { #pragma omp flush case 1: -#pragma omp flush // expected-error {{'#pragma omp flush' cannot be an immediate substatement}} +#pragma omp flush break; default: { #pragma omp flush diff --git a/test/OpenMP/target_codegen_registration.cpp b/test/OpenMP/target_codegen_registration.cpp index 0c9bba6df8bd..7d515bb64d90 100644 --- a/test/OpenMP/target_codegen_registration.cpp +++ b/test/OpenMP/target_codegen_registration.cpp @@ -407,31 +407,31 @@ int bar(int a){ // Check metadata is properly generated: // CHECK: !omp_offload.info = !{!{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}} -// CHECK-DAG = !{i32 0, i32 [[DEVID:[0-9]+]], i32 [[FILEID:-?[0-9]+]], !"_ZN2SB3fooEv", i32 160, i32 13, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2SDD2Ev", i32 210, i32 13, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2SEC2Ev", i32 226, i32 13, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2SED2Ev", i32 232, i32 13, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi1000EE3fooEv", i32 243, i32 13, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi100EEC2Ev", i32 249, i32 13, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_Z3bari", i32 352, i32 11, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi100EED2Ev", i32 255, i32 13, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi1000EEC2Ev", i32 249, i32 13, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi1000EED2Ev", i32 255, i32 13, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi100EE3fooEv", i32 243, i32 13, i32 {{[0-9]}}+} -// CHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2SCC2Ev", i32 185, i32 13, i32 {{[0-9]}}+} +// CHECK-DAG: = !{i32 0, i32 [[DEVID:-?[0-9]+]], i32 [[FILEID:-?[0-9]+]], !"_ZN2SB3fooEv", i32 193, i32 13, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2SDD1Ev", i32 243, i32 13, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2SEC1Ev", i32 259, i32 13, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2SED1Ev", i32 265, i32 13, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi1000EE3fooEv", i32 276, i32 13, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi100EEC1Ev", i32 282, i32 13, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_Z3bari", i32 402, i32 11, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi100EED1Ev", i32 288, i32 13, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi1000EEC1Ev", i32 282, i32 13, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi1000EED1Ev", i32 288, i32 13, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi100EE3fooEv", i32 276, i32 13, i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2SCC1Ev", i32 218, i32 13, i32 {{[0-9]+}}} // TCHECK: !omp_offload.info = !{!{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}, !{{[0-9]+}}} -// TCHECK-DAG = !{i32 0, i32 [[DEVID:[0-9]+]], i32 [[FILEID:-?[0-9]+]], !"_ZN2SB3fooEv", i32 160, i32 13, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2SDD2Ev", i32 210, i32 13, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2SEC2Ev", i32 226, i32 13, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2SED2Ev", i32 232, i32 13, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi1000EE3fooEv", i32 243, i32 13, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi100EEC2Ev", i32 249, i32 13, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_Z3bari", i32 352, i32 11, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi100EED2Ev", i32 255, i32 13, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi1000EEC2Ev", i32 249, i32 13, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi1000EED2Ev", i32 255, i32 13, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2STILi100EE3fooEv", i32 243, i32 13, i32 {{[0-9]}}+} -// TCHECK-DAG = !{i32 0, i32 [[DEVID]], i32 [[FILEID]] !"_ZN2SCC2Ev", i32 185, i32 13, i32 {{[0-9]}}+} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID:-?[0-9]+]], i32 [[FILEID:-?[0-9]+]], !"_ZN2SB3fooEv", i32 193, i32 13, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2SDD1Ev", i32 243, i32 13, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2SEC1Ev", i32 259, i32 13, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2SED1Ev", i32 265, i32 13, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi1000EE3fooEv", i32 276, i32 13, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi100EEC1Ev", i32 282, i32 13, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_Z3bari", i32 402, i32 11, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi100EED1Ev", i32 288, i32 13, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi1000EEC1Ev", i32 282, i32 13, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi1000EED1Ev", i32 288, i32 13, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2STILi100EE3fooEv", i32 276, i32 13, i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 [[DEVID]], i32 [[FILEID]], !"_ZN2SCC1Ev", i32 218, i32 13, i32 {{[0-9]+}}} #endif diff --git a/test/OpenMP/target_codegen_registration_naming.cpp b/test/OpenMP/target_codegen_registration_naming.cpp index 6ab9bf1aa94e..ab7a469aba4d 100644 --- a/test/OpenMP/target_codegen_registration_naming.cpp +++ b/test/OpenMP/target_codegen_registration_naming.cpp @@ -57,10 +57,10 @@ int nested(int a){ // Check metadata is properly generated: // CHECK: !omp_offload.info = !{!{{[0-9]+}}, !{{[0-9]+}}} -// CHECK-DAG: = !{i32 0, i32 {{[0-9]+}}, i32 {{-?[0-9]+}}, !"[[NNAME]]", i32 [[T1L]], i32 [[T1C]], i32 {{[0-9]+}}} -// CHECK-DAG: = !{i32 0, i32 {{[0-9]+}}, i32 {{-?[0-9]+}}, !"[[NNAME]]", i32 [[T2L]], i32 [[T2C]], i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 {{-?[0-9]+}}, i32 {{-?[0-9]+}}, !"[[NNAME]]", i32 [[T1L]], i32 [[T1C]], i32 {{[0-9]+}}} +// CHECK-DAG: = !{i32 0, i32 {{-?[0-9]+}}, i32 {{-?[0-9]+}}, !"[[NNAME]]", i32 [[T2L]], i32 [[T2C]], i32 {{[0-9]+}}} // TCHECK: !omp_offload.info = !{!{{[0-9]+}}, !{{[0-9]+}}} -// TCHECK-DAG: = !{i32 0, i32 {{[0-9]+}}, i32 {{-?[0-9]+}}, !"[[NNAME]]", i32 [[T1L]], i32 [[T1C]], i32 {{[0-9]+}}} -// TCHECK-DAG: = !{i32 0, i32 {{[0-9]+}}, i32 {{-?[0-9]+}}, !"[[NNAME]]", i32 [[T2L]], i32 [[T2C]], i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 {{-?[0-9]+}}, i32 {{-?[0-9]+}}, !"[[NNAME]]", i32 [[T1L]], i32 [[T1C]], i32 {{[0-9]+}}} +// TCHECK-DAG: = !{i32 0, i32 {{-?[0-9]+}}, i32 {{-?[0-9]+}}, !"[[NNAME]]", i32 [[T2L]], i32 [[T2C]], i32 {{[0-9]+}}} #endif diff --git a/test/OpenMP/taskwait_messages.cpp b/test/OpenMP/taskwait_messages.cpp index 084051354045..06e8e6b8bdf2 100644 --- a/test/OpenMP/taskwait_messages.cpp +++ b/test/OpenMP/taskwait_messages.cpp @@ -27,7 +27,7 @@ T tmain(T argc) { #pragma omp taskwait // expected-error {{'#pragma omp taskwait' cannot be an immediate substatement}} switch (argc) case 1: -#pragma omp taskwait // expected-error {{'#pragma omp taskwait' cannot be an immediate substatement}} +#pragma omp taskwait switch (argc) case 1: { #pragma omp taskwait @@ -35,7 +35,7 @@ T tmain(T argc) { switch (argc) { #pragma omp taskwait case 1: -#pragma omp taskwait // expected-error {{'#pragma omp taskwait' cannot be an immediate substatement}} +#pragma omp taskwait break; default: { #pragma omp taskwait @@ -81,7 +81,7 @@ int main(int argc, char **argv) { #pragma omp taskwait // expected-error {{'#pragma omp taskwait' cannot be an immediate substatement}} switch (argc) case 1: -#pragma omp taskwait // expected-error {{'#pragma omp taskwait' cannot be an immediate substatement}} +#pragma omp taskwait switch (argc) case 1: { #pragma omp taskwait @@ -89,7 +89,7 @@ int main(int argc, char **argv) { switch (argc) { #pragma omp taskwait case 1: -#pragma omp taskwait // expected-error {{'#pragma omp taskwait' cannot be an immediate substatement}} +#pragma omp taskwait break; default: { #pragma omp taskwait diff --git a/test/OpenMP/taskyield_messages.cpp b/test/OpenMP/taskyield_messages.cpp index 23c0b5339012..cfeaa63815b3 100644 --- a/test/OpenMP/taskyield_messages.cpp +++ b/test/OpenMP/taskyield_messages.cpp @@ -27,7 +27,7 @@ T tmain(T argc) { #pragma omp taskyield // expected-error {{'#pragma omp taskyield' cannot be an immediate substatement}} switch (argc) case 1: -#pragma omp taskyield // expected-error {{'#pragma omp taskyield' cannot be an immediate substatement}} +#pragma omp taskyield switch (argc) case 1: { #pragma omp taskyield @@ -35,7 +35,7 @@ T tmain(T argc) { switch (argc) { #pragma omp taskyield case 1: -#pragma omp taskyield // expected-error {{'#pragma omp taskyield' cannot be an immediate substatement}} +#pragma omp taskyield break; default: { #pragma omp taskyield @@ -81,7 +81,7 @@ int main(int argc, char **argv) { #pragma omp taskyield // expected-error {{'#pragma omp taskyield' cannot be an immediate substatement}} switch (argc) case 1: -#pragma omp taskyield // expected-error {{'#pragma omp taskyield' cannot be an immediate substatement}} +#pragma omp taskyield switch (argc) case 1: { #pragma omp taskyield @@ -89,7 +89,7 @@ int main(int argc, char **argv) { switch (argc) { #pragma omp taskyield case 1: -#pragma omp taskyield // expected-error {{'#pragma omp taskyield' cannot be an immediate substatement}} +#pragma omp taskyield break; default: { #pragma omp taskyield diff --git a/test/OpenMP/threadprivate_messages.cpp b/test/OpenMP/threadprivate_messages.cpp index f71d58bc52a4..8c442f47ad5e 100644 --- a/test/OpenMP/threadprivate_messages.cpp +++ b/test/OpenMP/threadprivate_messages.cpp @@ -118,6 +118,7 @@ int main(int argc, char **argv) { // expected-note {{'argc' defined here}} static double d1; static double d2; static double d3; // expected-note {{'d3' defined here}} + static double d4; static TestClass LocalClass(y); // expected-error {{variable with local storage in initial value of threadprivate variable}} #pragma omp threadprivate(LocalClass) @@ -133,6 +134,8 @@ int main(int argc, char **argv) { // expected-note {{'argc' defined here}} #pragma omp threadprivate(d3) // expected-error {{'#pragma omp threadprivate' must appear in the scope of the 'd3' variable declaration}} } #pragma omp threadprivate(d3) +label: +#pragma omp threadprivate(d4) // expected-error {{'#pragma omp threadprivate' cannot be an immediate substatement}} #pragma omp threadprivate(a) // expected-error {{'#pragma omp threadprivate' must appear in the scope of the 'a' variable declaration}} return (y); diff --git a/test/PCH/chain-default-argument-instantiation.cpp b/test/PCH/chain-default-argument-instantiation.cpp new file mode 100644 index 000000000000..0accd544a384 --- /dev/null +++ b/test/PCH/chain-default-argument-instantiation.cpp @@ -0,0 +1,50 @@ +// Test default argument instantiation in chained PCH. + +// Without PCH +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -include %s -include %s %s + +// With PCH +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s -chain-include %s -chain-include %s + +// With modules +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -fmodules %s -chain-include %s -chain-include %s + +// expected-no-diagnostics + +#ifndef HEADER1 +#define HEADER1 +//===----------------------------------------------------------------------===// +// Primary header. + +namespace rdar23810407 { + template<typename T> int f(T t) { + extern T rdar23810407_variable; + return 0; + } + template<typename T> int g(int a = f([] {})); +} + +//===----------------------------------------------------------------------===// +#elif not defined(HEADER2) +#define HEADER2 +#if !defined(HEADER1) +#error Header inclusion order messed up +#endif + +//===----------------------------------------------------------------------===// +// Dependent header. + +inline void instantiate_once() { + rdar23810407::g<int>(); +} + +//===----------------------------------------------------------------------===// +#else +//===----------------------------------------------------------------------===// + +void test() { + rdar23810407::g<int>(); +} + +//===----------------------------------------------------------------------===// +#endif diff --git a/test/PCH/cxx-char-literal.cpp b/test/PCH/cxx-char-literal.cpp new file mode 100644 index 000000000000..0990517c55be --- /dev/null +++ b/test/PCH/cxx-char-literal.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -emit-pch -std=c++1z -o %t %s +// RUN: %clang_cc1 -std=c++1z -x ast -ast-print %t | FileCheck %s + +// Ensure that character literals are properly surfaced through PCH. + +char a = '0'; +// CHECK: char a = '0'; + +char b = L'1'; +// CHECK: char b = L'1'; + +char c = u8'2'; +// CHECK: char c = u8'2'; + +char d = U'3'; +// CHECK: char d = U'3'; + +char e = u'4'; +// CHECK: char e = u'4'; diff --git a/test/PCH/ocl_types.cl b/test/PCH/ocl_types.cl index d788a3262143..21097c481182 100644 --- a/test/PCH/ocl_types.cl +++ b/test/PCH/ocl_types.cl @@ -1,9 +1,9 @@ // Test this without pch. -// RUN: %clang_cc1 -include %S/ocl_types.h -fsyntax-only %s +// RUN: %clang_cc1 -include %S/ocl_types.h -fsyntax-only %s -cl-std=CL2.0 -D__OPENCL_VERSION__=200 // Test with pch. -// RUN: %clang_cc1 -x cl -emit-pch -o %t %S/ocl_types.h -// RUN: %clang_cc1 -include-pch %t -fsyntax-only %s -ast-print +// RUN: %clang_cc1 -x cl -emit-pch -o %t %S/ocl_types.h -cl-std=CL2.0 -D__OPENCL_VERSION__=200 +// RUN: %clang_cc1 -include-pch %t -fsyntax-only %s -ast-print -cl-std=CL2.0 -D__OPENCL_VERSION__=200 void foo1(img1d_t img); @@ -24,3 +24,15 @@ void foo7(smp_t smp) { void foo8(evt_t evt) { evt_t loc_evt; } + +#if __OPENCL_VERSION__ >= 200 + +void foo9(pipe int P) { + int_pipe_function(P); +} + +void foo10(pipe Person P) { + person_pipe_function(P); +} + +#endif diff --git a/test/PCH/ocl_types.h b/test/PCH/ocl_types.h index 93cf4f66442b..bdc4bb11c1b2 100644 --- a/test/PCH/ocl_types.h +++ b/test/PCH/ocl_types.h @@ -44,6 +44,7 @@ typedef image2d_depth_t img2ddep_t; // image2d_array_depth_t typedef image2d_array_depth_t img2darr_dep_t; +#pragma OPENCL EXTENSION cl_khr_gl_msaa_sharing : enable // image2d_msaa_t typedef image2d_msaa_t img2dmsaa_t; @@ -56,4 +57,14 @@ typedef image2d_msaa_depth_t img2dmsaadep_t; // image2d_array_msaa_depth_t typedef image2d_array_msaa_depth_t img2darrmsaadep_t; +// pipe specifier + +typedef struct _person { + int id; + const char *name; +} Person; + +void int_pipe_function(pipe int); + +void person_pipe_function(pipe Person); #endif diff --git a/test/Sema/bitfield-layout.c b/test/Sema/bitfield-layout.c index 2abd139d238f..b96b38686417 100644 --- a/test/Sema/bitfield-layout.c +++ b/test/Sema/bitfield-layout.c @@ -1,13 +1,25 @@ // RUN: %clang_cc1 %s -fsyntax-only -verify -triple=i686-apple-darwin9 +// RUN: %clang_cc1 %s -fsyntax-only -verify -triple=arm-linux-gnueabihf +// RUN: %clang_cc1 %s -fsyntax-only -verify -triple=aarch64-linux-gnu // expected-no-diagnostics +#include <stddef.h> -#define CHECK_SIZE(kind, name, size) extern int name##1[sizeof(kind name) == size ? 1 : -1]; -#define CHECK_ALIGN(kind, name, size) extern int name##2[__alignof(kind name) == size ? 1 : -1]; +#define CHECK_SIZE(kind, name, size) \ + extern int name##_1[sizeof(kind name) == size ? 1 : -1]; +#define CHECK_ALIGN(kind, name, size) \ + extern int name##_2[__alignof(kind name) == size ? 1 : -1]; +#define CHECK_OFFSET(kind, name, member, offset) \ + extern int name##_3[offsetof(kind name, member) == offset ? 1 : -1]; // Zero-width bit-fields struct a {char x; int : 0; char y;}; +#if defined(__arm__) || defined(__aarch64__) +CHECK_SIZE(struct, a, 8) +CHECK_ALIGN(struct, a, 4) +#else CHECK_SIZE(struct, a, 5) CHECK_ALIGN(struct, a, 1) +#endif // Zero-width bit-fields with packed struct __attribute__((packed)) a2 { short x : 9; char : 0; int y : 17; }; @@ -16,8 +28,13 @@ CHECK_ALIGN(struct, a2, 1) // Zero-width bit-fields at the end of packed struct struct __attribute__((packed)) a3 { short x : 9; int : 0; }; +#if defined(__arm__) || defined(__aarch64__) +CHECK_SIZE(struct, a3, 4) +CHECK_ALIGN(struct, a3, 4) +#else CHECK_SIZE(struct, a3, 4) CHECK_ALIGN(struct, a3, 1) +#endif // For comparison, non-zero-width bit-fields at the end of packed struct struct __attribute__((packed)) a4 { short x : 9; int : 1; }; @@ -25,17 +42,32 @@ CHECK_SIZE(struct, a4, 2) CHECK_ALIGN(struct, a4, 1) union b {char x; int : 0; char y;}; +#if defined(__arm__) || defined(__aarch64__) +CHECK_SIZE(union, b, 4) +CHECK_ALIGN(union, b, 4) +#else CHECK_SIZE(union, b, 1) CHECK_ALIGN(union, b, 1) +#endif // Unnamed bit-field align struct c {char x; int : 20;}; +#if defined(__arm__) || defined(__aarch64__) +CHECK_SIZE(struct, c, 4) +CHECK_ALIGN(struct, c, 4) +#else CHECK_SIZE(struct, c, 4) CHECK_ALIGN(struct, c, 1) +#endif union d {char x; int : 20;}; +#if defined(__arm__) || defined(__aarch64__) +CHECK_SIZE(union, d, 4) +CHECK_ALIGN(union, d, 4) +#else CHECK_SIZE(union, d, 3) CHECK_ALIGN(union, d, 1) +#endif // Bit-field packing struct __attribute__((packed)) e {int x : 4, y : 30, z : 30;}; @@ -56,3 +88,153 @@ struct s0 { CHECK_SIZE(struct, s0, 0x32100008) CHECK_ALIGN(struct, s0, 4) +// Bit-field with explicit align bigger than normal. +struct g0 { + char a; + __attribute__((aligned(16))) int b : 1; + char c; +}; + +CHECK_SIZE(struct, g0, 32); +CHECK_ALIGN(struct, g0, 16); +CHECK_OFFSET(struct, g0, c, 17); + +// Bit-field with explicit align smaller than normal. +struct g1 { + char a; + __attribute__((aligned(2))) int b : 1; + char c; +}; + +CHECK_SIZE(struct, g1, 4); +CHECK_ALIGN(struct, g1, 4); +CHECK_OFFSET(struct, g1, c, 3); + +// Same as above but without explicit align. +struct g2 { + char a; + int b : 1; + char c; +}; + +CHECK_SIZE(struct, g2, 4); +CHECK_ALIGN(struct, g2, 4); +CHECK_OFFSET(struct, g2, c, 2); + +// Explicit attribute align on bit-field has precedence over packed attribute +// applied too the struct. +struct __attribute__((packed)) g3 { + char a; + __attribute__((aligned(16))) int b : 1; + char c; +}; + +CHECK_SIZE(struct, g3, 32); +CHECK_ALIGN(struct, g3, 16); +CHECK_OFFSET(struct, g3, c, 17); + +struct __attribute__((packed)) g4 { + char a; + __attribute__((aligned(2))) int b : 1; + char c; +}; + +CHECK_SIZE(struct, g4, 4); +CHECK_ALIGN(struct, g4, 2); +CHECK_OFFSET(struct, g4, c, 3); + +struct g5 { + char : 1; + __attribute__((aligned(1))) int n : 24; +}; +CHECK_SIZE(struct, g5, 4); +CHECK_ALIGN(struct, g5, 4); + +struct __attribute__((packed)) g6 { + char : 1; + __attribute__((aligned(1))) int n : 24; +}; +CHECK_SIZE(struct, g6, 4); +CHECK_ALIGN(struct, g6, 1); + +struct g7 { + char : 1; + __attribute__((aligned(1))) int n : 25; +}; +CHECK_SIZE(struct, g7, 8); +CHECK_ALIGN(struct, g7, 4); + +struct __attribute__((packed)) g8 { + char : 1; + __attribute__((aligned(1))) int n : 25; +}; +CHECK_SIZE(struct, g8, 5); +CHECK_ALIGN(struct, g8, 1); + +struct g9 { + __attribute__((aligned(1))) char a : 2, b : 2, c : 2, d : 2, e : 2; + int i; +}; +CHECK_SIZE(struct, g9, 12); +CHECK_ALIGN(struct, g9, 4); + +struct __attribute__((packed)) g10 { + __attribute__((aligned(1))) char a : 2, b : 2, c : 2, d : 2, e : 2; + int i; +}; +CHECK_SIZE(struct, g10, 9); +CHECK_ALIGN(struct, g10, 1); + +struct g11 { + char a; + __attribute__((aligned(1))) long long b : 62; + char c; +}; +#if defined(__arm__) || defined(__aarch64__) +CHECK_SIZE(struct, g11, 24); +CHECK_ALIGN(struct, g11, 8); +CHECK_OFFSET(struct, g11, c, 16); +#else +CHECK_SIZE(struct, g11, 16); +CHECK_ALIGN(struct, g11, 4); +CHECK_OFFSET(struct, g11, c, 12); +#endif + +struct __attribute__((packed)) g12 { + char a; + __attribute__((aligned(1))) long long b : 62; + char c; +}; +CHECK_SIZE(struct, g12, 10); +CHECK_ALIGN(struct, g12, 1); +CHECK_OFFSET(struct, g12, c, 9); + +struct g13 { + char a; + __attribute__((aligned(1))) long long : 0; + char c; +}; +#if defined(__arm__) || defined(__aarch64__) +CHECK_SIZE(struct, g13, 16); +CHECK_ALIGN(struct, g13, 8); +CHECK_OFFSET(struct, g13, c, 8); +#else +CHECK_SIZE(struct, g13, 5); +CHECK_ALIGN(struct, g13, 1); +CHECK_OFFSET(struct, g13, c, 4); +#endif + +struct __attribute__((packed)) g14 { + char a; + __attribute__((aligned(1))) long long : 0; + char c; +}; +#if defined(__arm__) || defined(__aarch64__) +CHECK_SIZE(struct, g14, 16); +CHECK_ALIGN(struct, g14, 8); +CHECK_OFFSET(struct, g14, c, 8); +#else +CHECK_SIZE(struct, g14, 5); +CHECK_ALIGN(struct, g14, 1); +CHECK_OFFSET(struct, g14, c, 4); +#endif diff --git a/test/Sema/darwin-tls.c b/test/Sema/darwin-tls.c new file mode 100644 index 000000000000..0fcf096d92ba --- /dev/null +++ b/test/Sema/darwin-tls.c @@ -0,0 +1,17 @@ +// RUN: not %clang_cc1 -fsyntax-only -triple x86_64-apple-macosx10.6 %s 2>&1 | FileCheck %s --check-prefix NO-TLS +// RUN: %clang_cc1 -fsyntax-only -triple x86_64-apple-macosx10.7 %s 2>&1 | FileCheck %s --check-prefix TLS +// RUN: not %clang_cc1 -fsyntax-only -triple arm64-apple-ios7.1 %s 2>&1 | FileCheck %s --check-prefix NO-TLS +// RUN: %clang_cc1 -fsyntax-only -triple arm64-apple-ios8.0 %s 2>&1 | FileCheck %s --check-prefix TLS +// RUN: not %clang_cc1 -fsyntax-only -triple thumbv7s-apple-ios8.3 %s 2>&1 | FileCheck %s --check-prefix NO-TLS +// RUN: %clang_cc1 -fsyntax-only -triple thumbv7s-apple-ios9.0 %s 2>&1 | FileCheck %s --check-prefix TLS +// RUN: %clang_cc1 -fsyntax-only -triple armv7-apple-ios9.0 %s 2>&1 | FileCheck %s --check-prefix TLS +// RUN: not %clang_cc1 -fsyntax-only -triple thumbv7k-apple-watchos1.0 %s 2>&1 | FileCheck %s --check-prefix NO-TLS +// RUN: %clang_cc1 -fsyntax-only -triple thumbv7k-apple-watchos2.0 %s 2>&1 | FileCheck %s --check-prefix TLS + + +__thread int a; + +// NO-TLS: thread-local storage is not supported for the current target +// TLS-NOT: thread-local storage is not supported for the current target + +wibble; diff --git a/test/Sema/decl-in-prototype.c b/test/Sema/decl-in-prototype.c index 4f581aa54e53..3b8a3b860371 100644 --- a/test/Sema/decl-in-prototype.c +++ b/test/Sema/decl-in-prototype.c @@ -35,3 +35,6 @@ void f6(struct z {int b;} c) { // expected-warning {{declaration of 'struct z' w void pr19018_1 (enum e19018 { qq } x); // expected-warning{{declaration of 'enum e19018' will not be visible outside of this function}} enum e19018 qq; //expected-error{{tentative definition has type 'enum e19018' that is never completed}} \ //expected-note{{forward declaration of 'enum e19018'}} + +// Only warn once, even if we create two declarations. +void f(struct q *, struct __attribute__((aligned(4))) q *); // expected-warning {{will not be visible outside}} diff --git a/test/Sema/integer-overflow.c b/test/Sema/integer-overflow.c index 44fbcd4c818e..db5c1f4c7118 100644 --- a/test/Sema/integer-overflow.c +++ b/test/Sema/integer-overflow.c @@ -145,3 +145,11 @@ uint64_t check_integer_overflows(int i) { // expected-warning@+1 2{{overflow in expression; result is 536870912 with type 'int'}} return ((4608 * 1024 * 1024) + ((uint64_t)(4608 * 1024 * 1024))); } + +struct s { + unsigned x; + unsigned y; +} s = { + .y = 5, + .x = 4 * 1024 * 1024 * 1024 // expected-warning {{overflow in expression; result is 0 with type 'int'}} +}; diff --git a/test/Sema/pass-object-size.c b/test/Sema/pass-object-size.c index e4f460b1785f..6f375c0e94d5 100644 --- a/test/Sema/pass-object-size.c +++ b/test/Sema/pass-object-size.c @@ -33,7 +33,7 @@ void TakeFnOvl(void (*)(int *)) overloaded; void NotOverloaded(void *p PS(0)); void IsOverloaded(void *p PS(0)) overloaded; -void IsOverloaded(char *p) overloaded; +void IsOverloaded(char *p) overloaded; // char* inestead of void* is intentional void FunctionPtrs() { void (*p)(void *) = NotOverloaded; //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} void (*p2)(void *) = &NotOverloaded; //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} @@ -49,4 +49,8 @@ void FunctionPtrs() { TakeFnOvl(NotOverloaded); //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} TakeFnOvl(&NotOverloaded); //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} + + int P; + (&NotOverloaded)(&P); //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} + (&IsOverloaded)(&P); //expected-error{{no matching function}} expected-note@35{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@36{{candidate function not viable: no known conversion from 'int *' to 'char *' for 1st argument}} } diff --git a/test/SemaCUDA/Inputs/cuda.h b/test/SemaCUDA/Inputs/cuda.h index a9a4595a14a9..18cafdf96af8 100644 --- a/test/SemaCUDA/Inputs/cuda.h +++ b/test/SemaCUDA/Inputs/cuda.h @@ -2,6 +2,9 @@ #include <stddef.h> +// Make this file work with nvcc, for testing compatibility. + +#ifndef __NVCC__ #define __constant__ __attribute__((constant)) #define __device__ __attribute__((device)) #define __global__ __attribute__((global)) @@ -18,3 +21,4 @@ typedef struct cudaStream *cudaStream_t; int cudaConfigureCall(dim3 gridSize, dim3 blockSize, size_t sharedSize = 0, cudaStream_t stream = 0); +#endif // !__NVCC__ diff --git a/test/SemaCUDA/attributes.cu b/test/SemaCUDA/attributes-on-non-cuda.cu index ce4dc925a3f3..e9e32ce658cb 100644 --- a/test/SemaCUDA/attributes.cu +++ b/test/SemaCUDA/attributes-on-non-cuda.cu @@ -1,4 +1,5 @@ -// Tests handling of CUDA attributes. +// Tests that CUDA attributes are warnings when compiling C files, but not when +// compiling CUDA files. // // RUN: %clang_cc1 -fsyntax-only -verify %s // RUN: %clang_cc1 -fsyntax-only -fcuda-is-device -verify %s diff --git a/test/SemaCUDA/bad-attributes.cu b/test/SemaCUDA/bad-attributes.cu new file mode 100644 index 000000000000..7e01e141de1d --- /dev/null +++ b/test/SemaCUDA/bad-attributes.cu @@ -0,0 +1,49 @@ +// Tests handling of CUDA attributes that are bad either because they're +// applied to the wrong sort of thing, or because they're given in illegal +// combinations. +// +// You should be able to run this file through nvcc for compatibility testing. +// +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fcuda-is-device -fsyntax-only -verify %s + +#include "Inputs/cuda.h" + +// Try applying attributes to functions and variables. Some should generate +// warnings; others not. +__device__ int a1; +__device__ void a2(); +__host__ int b1; // expected-warning {{attribute only applies to functions}} +__host__ void b2(); +__constant__ int c1; +__constant__ void c2(); // expected-warning {{attribute only applies to variables}} +__shared__ int d1; +__shared__ void d2(); // expected-warning {{attribute only applies to variables}} +__global__ int e1; // expected-warning {{attribute only applies to functions}} +__global__ void e2(); + +// Try all pairs of attributes which can be present on a function or a +// variable. Check both orderings of the attributes, as that can matter in +// clang. +__device__ __host__ void z1(); +__device__ __constant__ int z2; +__device__ __shared__ int z3; +__device__ __global__ void z4(); // expected-error {{attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} + +__host__ __device__ void z5(); +__host__ __global__ void z6(); // expected-error {{attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} + +__constant__ __device__ int z7; +__constant__ __shared__ int z8; // expected-error {{attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} + +__shared__ __device__ int z9; +__shared__ __constant__ int z10; // expected-error {{attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} + +__global__ __device__ void z11(); // expected-error {{attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} +__global__ __host__ void z12(); // expected-error {{attributes are not compatible}} +// expected-note@-1 {{conflicting attribute is here}} diff --git a/test/SemaCXX/anonymous-union.cpp b/test/SemaCXX/anonymous-union.cpp index 3520245a03a8..0b654266f75f 100644 --- a/test/SemaCXX/anonymous-union.cpp +++ b/test/SemaCXX/anonymous-union.cpp @@ -62,11 +62,11 @@ void test_unqual_references(X x, const X xc) { struct Redecl { int x; // expected-note{{previous declaration is here}} - class y { }; + class y { }; // expected-note{{previous declaration is here}} union { int x; // expected-error{{member of anonymous union redeclares 'x'}} - float y; + float y; // expected-error{{member of anonymous union redeclares 'y'}} double z; // expected-note{{previous declaration is here}} double zz; // expected-note{{previous definition is here}} }; diff --git a/test/SemaCXX/conversion.cpp b/test/SemaCXX/conversion.cpp index b8f0e076a2bc..4c4089c6aae7 100644 --- a/test/SemaCXX/conversion.cpp +++ b/test/SemaCXX/conversion.cpp @@ -77,30 +77,8 @@ void test3() { // CHECK: note: expanded from macro 'FINIT' #define FINIT int a3 = NULL; FINIT // expected-warning {{implicit conversion of NULL constant to 'int'}} - - // we don't catch the case of #define FOO NULL ... int i = FOO; but that seems a bit narrow anyway - // and avoiding that helps us skip these cases: -#define NULL_COND(cond) ((cond) ? &a : NULL) - bool bl2 = NULL_COND(true); // don't warn on NULL conversion through the conditional operator across a macro boundary - if (NULL_COND(true)) - ; - while (NULL_COND(true)) - ; - for (; NULL_COND(true); ) - ; - do ; - while(NULL_COND(true)); - -#define NULL_WRAPPER NULL_COND(false) - if (NULL_WRAPPER) - ; - while (NULL_WRAPPER) - ; - for (; NULL_WRAPPER;) - ; - do - ; - while (NULL_WRAPPER); + // we don't catch the case of #define FOO NULL ... int i = FOO; but that + // seems a bit narrow anyway and avoiding that helps us skip other cases. int *ip = NULL; int (*fp)() = NULL; @@ -116,9 +94,9 @@ namespace test4 { // FIXME: We should warn for non-dependent args (only when the param type is also non-dependent) only once // not once for the template + once for every instantiation template<typename T> - void tmpl(char c = NULL, // expected-warning 4 {{implicit conversion of NULL constant to 'char'}} + void tmpl(char c = NULL, // expected-warning 3 {{implicit conversion of NULL constant to 'char'}} T a = NULL, // expected-warning {{implicit conversion of NULL constant to 'char'}} \ - expected-warning 2 {{implicit conversion of NULL constant to 'int'}} + expected-warning {{implicit conversion of NULL constant to 'int'}} T b = 1024) { // expected-warning {{implicit conversion from 'int' to 'char' changes value from 1024 to 0}} } @@ -129,8 +107,7 @@ namespace test4 { void func() { tmpl<char>(); // expected-note 2 {{in instantiation of default function argument expression for 'tmpl<char>' required here}} tmpl<int>(); // expected-note 2 {{in instantiation of default function argument expression for 'tmpl<int>' required here}} - // FIXME: We should warn only once for each template instantiation - not once for each call - tmpl<int>(); // expected-note 2 {{in instantiation of default function argument expression for 'tmpl<int>' required here}} + tmpl<int>(); tmpl2<int*>(); } } @@ -157,3 +134,97 @@ namespace test7 { return nullptr; // expected-warning {{implicit conversion of nullptr constant to 'bool'}} } } + +namespace test8 { + #define NULL_COND(cond) ((cond) ? &num : NULL) + #define NULL_WRAPPER NULL_COND(false) + + // don't warn on NULL conversion through the conditional operator across a + // macro boundary + void macro() { + int num; + bool b = NULL_COND(true); + if (NULL_COND(true)) {} + while (NULL_COND(true)) {} + for (;NULL_COND(true);) {} + do {} while (NULL_COND(true)); + + if (NULL_WRAPPER) {} + while (NULL_WRAPPER) {} + for (;NULL_WRAPPER;) {} + do {} while (NULL_WRAPPER); + } + + // Identical to the previous function except with a template argument. + // This ensures that template instantiation does not introduce any new + // warnings. + template <typename X> + void template_and_macro() { + int num; + bool b = NULL_COND(true); + if (NULL_COND(true)) {} + while (NULL_COND(true)) {} + for (;NULL_COND(true);) {} + do {} while (NULL_COND(true)); + + if (NULL_WRAPPER) {} + while (NULL_WRAPPER) {} + for (;NULL_WRAPPER;) {} + do {} while (NULL_WRAPPER); + } + + // Identical to the previous function except the template argument affects + // the conditional statement. + template <typename X> + void template_and_macro2() { + X num; + bool b = NULL_COND(true); + if (NULL_COND(true)) {} + while (NULL_COND(true)) {} + for (;NULL_COND(true);) {} + do {} while (NULL_COND(true)); + + if (NULL_WRAPPER) {} + while (NULL_WRAPPER) {} + for (;NULL_WRAPPER;) {} + do {} while (NULL_WRAPPER); + } + + void run() { + template_and_macro<int>(); + template_and_macro<double>(); + template_and_macro2<int>(); + template_and_macro2<double>(); + } +} + +// Don't warn on a nullptr to bool conversion when the nullptr is the return +// type of a function. +namespace test9 { + typedef decltype(nullptr) nullptr_t; + nullptr_t EXIT(); + + bool test() { + return EXIT(); + } +} + +// Test NULL macro inside a macro has same warnings nullptr inside a macro. +namespace test10 { +#define test1(cond) \ + ((cond) ? nullptr : NULL) +#define test2(cond) \ + ((cond) ? NULL : nullptr) + +#define assert(cond) \ + ((cond) ? foo() : bar()) + void foo(); + void bar(); + + void run(int x) { + if (test1(x)) {} + if (test2(x)) {} + assert(test1(x)); + assert(test2(x)); + } +} diff --git a/test/SemaCXX/linkage-invalid-decl.cpp b/test/SemaCXX/linkage-invalid-decl.cpp new file mode 100644 index 000000000000..0a991baf9d7d --- /dev/null +++ b/test/SemaCXX/linkage-invalid-decl.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// This invalid declaration used to call infinite recursion in linkage +// calculation for enum as a function argument. +inline foo(A)(enum E; +// expected-error@-1 {{unknown type name 'foo'}} +// expected-error@-2 {{ISO C++ forbids forward references to 'enum' types}} +// expected-error@-3 {{expected ')'}} +// expected-note@-4 {{to match this '('}} diff --git a/test/SemaCXX/pass-object-size.cpp b/test/SemaCXX/pass-object-size.cpp index bec0c1c55f29..94c9cc9c8aa0 100644 --- a/test/SemaCXX/pass-object-size.cpp +++ b/test/SemaCXX/pass-object-size.cpp @@ -120,3 +120,17 @@ void Bar() { (void)+[](void *const p __attribute__((pass_object_size(0)))) {}; //expected-error-re{{invalid argument type '(lambda at {{.*}})' to unary expression}} } } + +namespace ovlbug { +// Directly calling an address-of function expression (e.g. in (&foo)(args...)) +// doesn't go through regular address-of-overload logic. This caused the above +// code to generate an ICE. +void DirectAddrOf(void *__attribute__((pass_object_size(0)))); +void DirectAddrOfOvl(void *__attribute__((pass_object_size(0)))); +void DirectAddrOfOvl(int *); + +void Test() { + (&DirectAddrOf)(nullptr); //expected-error{{cannot take address of function 'DirectAddrOf' because parameter 1 has pass_object_size attribute}} + (&DirectAddrOfOvl)((char*)nullptr); //expected-error{{no matching function}} expected-note@129{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@130{{candidate function not viable: no known conversion from 'char *' to 'int *' for 1st argument}} +} +} diff --git a/test/SemaOpenCL/invalid-pipes-cl2.0.cl b/test/SemaOpenCL/invalid-pipes-cl2.0.cl new file mode 100644 index 000000000000..ee36892b93d4 --- /dev/null +++ b/test/SemaOpenCL/invalid-pipes-cl2.0.cl @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only -cl-std=CL2.0 + +void test1(pipe int *p){// expected-error {{pipes packet types cannot be of reference type}} +} +void test2(pipe p){// expected-error {{missing actual type specifier for pipe}} +} +void test3(int pipe p){// expected-error {{cannot combine with previous 'int' declaration specifier}} +} diff --git a/test/SemaOpenCL/pipes-1.2-negative.cl b/test/SemaOpenCL/pipes-1.2-negative.cl new file mode 100644 index 000000000000..441a24cf8522 --- /dev/null +++ b/test/SemaOpenCL/pipes-1.2-negative.cl @@ -0,0 +1,3 @@ +// RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only -cl-std=CL1.2 + +void foo(read_only pipe int p); // expected-error {{expected parameter declarator}} expected-error {{expected ')'}} expected-note {{to match this '('}} diff --git a/test/SemaTemplate/default-arguments-cxx0x.cpp b/test/SemaTemplate/default-arguments-cxx0x.cpp index 4cfd7a5843f0..0c97c2056b75 100644 --- a/test/SemaTemplate/default-arguments-cxx0x.cpp +++ b/test/SemaTemplate/default-arguments-cxx0x.cpp @@ -56,3 +56,22 @@ namespace PR16975 { baz data{0}; } + +// rdar://23810407 +// An IRGen failure due to a symbol collision due to a default argument +// being instantiated twice. Credit goes to Richard Smith for this +// reduction to a -fsyntax-only failure. +namespace rdar23810407 { + // Instantiating the default argument multiple times will produce two + // different lambda types and thus instantiate this function multiple + // times, which will produce conflicting extern variable declarations. + template<typename T> int f(T t) { + extern T rdar23810407_variable; + return 0; + } + template<typename T> int g(int a = f([] {})); + void test() { + g<int>(); + g<int>(); + } +} diff --git a/test/SemaTemplate/temp_arg_template.cpp b/test/SemaTemplate/temp_arg_template.cpp index 4a0ed05d8799..6d93f1504a46 100644 --- a/test/SemaTemplate/temp_arg_template.cpp +++ b/test/SemaTemplate/temp_arg_template.cpp @@ -75,7 +75,11 @@ template <template <typename> class... Templates> // expected-warning@-2 {{variadic templates are a C++11 extension}} #endif -struct template_tuple {}; +struct template_tuple { +#if __cplusplus >= 201103L + static constexpr int N = sizeof...(Templates); +#endif +}; template <typename T> struct identity {}; template <template <typename> class... Templates> @@ -85,6 +89,12 @@ template <template <typename> class... Templates> template_tuple<Templates...> f7() {} +#if __cplusplus >= 201103L +struct S : public template_tuple<identity, identity> { + static_assert(N == 2, "Number of template arguments incorrect"); +}; +#endif + void foo() { f7<identity>(); } diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index dba676a1f756..510bc44f40cb 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -5,8 +5,6 @@ add_clang_subdirectory(driver) add_clang_subdirectory(clang-format) add_clang_subdirectory(clang-format-vs) add_clang_subdirectory(clang-fuzzer) -add_clang_subdirectory(scan-build) -add_clang_subdirectory(scan-view) add_clang_subdirectory(c-index-test) add_clang_subdirectory(libclang) @@ -18,6 +16,8 @@ endif() if(CLANG_ENABLE_STATIC_ANALYZER) add_clang_subdirectory(clang-check) + add_clang_subdirectory(scan-build) + add_clang_subdirectory(scan-view) endif() # We support checking out the clang-tools-extra repository into the 'extra' diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index 8336491c0158..2a6002537ec5 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -1508,6 +1508,22 @@ static enum CXChildVisitResult PrintBitWidth(CXCursor cursor, CXCursor p, } /******************************************************************************/ +/* Type declaration testing */ +/******************************************************************************/ + +static enum CXChildVisitResult PrintTypeDeclaration(CXCursor cursor, CXCursor p, + CXClientData d) { + CXCursor typeDeclaration = clang_getTypeDeclaration(clang_getCursorType(cursor)); + + if (clang_isDeclaration(typeDeclaration.kind)) { + PrintCursor(cursor, NULL); + PrintTypeAndTypeKind(clang_getCursorType(typeDeclaration), " [typedeclaration=%s] [typekind=%s]\n"); + } + + return CXChildVisit_Recurse; +} + +/******************************************************************************/ /* Loading ASTs/source. */ /******************************************************************************/ @@ -4137,6 +4153,7 @@ static void print_usage(void) { " c-index-test -test-print-type {<args>}*\n" " c-index-test -test-print-type-size {<args>}*\n" " c-index-test -test-print-bitwidth {<args>}*\n" + " c-index-test -test-print-type-declaration {<args>}*\n" " c-index-test -print-usr [<CursorKind> {<args>}]*\n" " c-index-test -print-usr-file <file>\n" " c-index-test -write-pch <file> <compiler arguments>\n"); @@ -4230,6 +4247,9 @@ int cindextest_main(int argc, const char **argv) { else if (argc > 2 && strcmp(argv[1], "-test-print-type-size") == 0) return perform_test_load_source(argc - 2, argv + 2, "all", PrintTypeSize, 0); + else if (argc > 2 && strcmp(argv[1], "-test-print-type-declaration") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", + PrintTypeDeclaration, 0); else if (argc > 2 && strcmp(argv[1], "-test-print-bitwidth") == 0) return perform_test_load_source(argc - 2, argv + 2, "all", PrintBitWidth, 0); diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index dbda13c23532..9086c60e18be 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -1686,6 +1686,10 @@ bool CursorVisitor::VisitAtomicTypeLoc(AtomicTypeLoc TL) { return Visit(TL.getValueLoc()); } +bool CursorVisitor::VisitPipeTypeLoc(PipeTypeLoc TL) { + return Visit(TL.getValueLoc()); +} + #define DEFAULT_TYPELOC_IMPL(CLASS, PARENT) \ bool CursorVisitor::Visit##CLASS##TypeLoc(CLASS##TypeLoc TL) { \ return Visit##PARENT##Loc(TL); \ diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index 72c12cd16b97..44bb631f7866 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -412,6 +412,12 @@ try_again: .getAsTemplateDecl(); break; + case Type::Auto: + TP = cast<AutoType>(TP)->getDeducedType().getTypePtrOrNull(); + if (TP) + goto try_again; + break; + case Type::InjectedClassName: D = cast<InjectedClassNameType>(TP)->getDecl(); break; diff --git a/tools/scan-build-py/README.md b/tools/scan-build-py/README.md new file mode 100644 index 000000000000..54bfc37ff76d --- /dev/null +++ b/tools/scan-build-py/README.md @@ -0,0 +1,120 @@ +scan-build +========== + +A package designed to wrap a build so that all calls to gcc/clang are +intercepted and logged into a [compilation database][1] and/or piped to +the clang static analyzer. Includes intercept-build tool, which logs +the build, as well as scan-build tool, which logs the build and runs +the clang static analyzer on it. + +Portability +----------- + +Should be working on UNIX operating systems. + +- It has been tested on FreeBSD, GNU/Linux and OS X. +- Prepared to work on windows, but need help to make it. + + +Prerequisites +------------- + +1. **python** interpreter (version 2.7, 3.2, 3.3, 3.4, 3.5). + + +How to use +---------- + +To run the Clang static analyzer against a project goes like this: + + $ scan-build <your build command> + +To generate a compilation database file goes like this: + + $ intercept-build <your build command> + +To run the Clang static analyzer against a project with compilation database +goes like this: + + $ analyze-build + +Use `--help` to know more about the commands. + + +Limitations +----------- + +Generally speaking, the `intercept-build` and `analyze-build` tools together +does the same job as `scan-build` does. So, you can expect the same output +from this line as simple `scan-build` would do: + + $ intercept-build <your build command> && analyze-build + +The major difference is how and when the analyzer is run. The `scan-build` +tool has three distinct model to run the analyzer: + +1. Use compiler wrappers to make actions. + The compiler wrappers does run the real compiler and the analyzer. + This is the default behaviour, can be enforced with `--override-compiler` + flag. + +2. Use special library to intercept compiler calls durring the build process. + The analyzer run against each modules after the build finished. + Use `--intercept-first` flag to get this model. + +3. Use compiler wrappers to intercept compiler calls durring the build process. + The analyzer run against each modules after the build finished. + Use `--intercept-first` and `--override-compiler` flags together to get + this model. + +The 1. and 3. are using compiler wrappers, which works only if the build +process respects the `CC` and `CXX` environment variables. (Some build +process can override these variable as command line parameter only. This case +you need to pass the compiler wrappers manually. eg.: `intercept-build +--override-compiler make CC=intercept-cc CXX=intercept-c++ all` where the +original build command would have been `make all` only.) + +The 1. runs the analyzer right after the real compilation. So, if the build +process removes removes intermediate modules (generated sources) the analyzer +output still kept. + +The 2. and 3. generate the compilation database first, and filters out those +modules which are not exists. So, it's suitable for incremental analysis durring +the development. + +The 2. mode is available only on FreeBSD and Linux. Where library preload +is available from the dynamic loader. Not supported on OS X (unless System +Integrity Protection feature is turned off). + +`intercept-build` command uses only the 2. and 3. mode to generate the +compilation database. `analyze-build` does only run the analyzer against the +captured compiler calls. + + +Known problems +-------------- + +Because it uses `LD_PRELOAD` or `DYLD_INSERT_LIBRARIES` environment variables, +it does not append to it, but overrides it. So builds which are using these +variables might not work. (I don't know any build tool which does that, but +please let me know if you do.) + + +Problem reports +--------------- + +If you find a bug in this documentation or elsewhere in the program or would +like to propose an improvement, please use the project's [issue tracker][3]. +Please describing the bug and where you found it. If you have a suggestion +how to fix it, include that as well. Patches are also welcome. + + +License +------- + +The project is licensed under University of Illinois/NCSA Open Source License. +See LICENSE.TXT for details. + + [1]: http://clang.llvm.org/docs/JSONCompilationDatabase.html + [2]: https://pypi.python.org/pypi/scan-build + [3]: https://llvm.org/bugs/enter_bug.cgi?product=clang diff --git a/tools/scan-build-py/bin/analyze-build b/tools/scan-build-py/bin/analyze-build new file mode 100644 index 000000000000..2cc9676fd546 --- /dev/null +++ b/tools/scan-build-py/bin/analyze-build @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import multiprocessing +multiprocessing.freeze_support() + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.analyze import analyze_build_main +sys.exit(analyze_build_main(this_dir, False)) diff --git a/tools/scan-build-py/bin/analyze-c++ b/tools/scan-build-py/bin/analyze-c++ new file mode 100644 index 000000000000..15186d89aa3f --- /dev/null +++ b/tools/scan-build-py/bin/analyze-c++ @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.analyze import analyze_build_wrapper +sys.exit(analyze_build_wrapper(True)) diff --git a/tools/scan-build-py/bin/analyze-cc b/tools/scan-build-py/bin/analyze-cc new file mode 100644 index 000000000000..55519fb7b11d --- /dev/null +++ b/tools/scan-build-py/bin/analyze-cc @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.analyze import analyze_build_wrapper +sys.exit(analyze_build_wrapper(False)) diff --git a/tools/scan-build-py/bin/intercept-build b/tools/scan-build-py/bin/intercept-build new file mode 100644 index 000000000000..164f2e68be93 --- /dev/null +++ b/tools/scan-build-py/bin/intercept-build @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import multiprocessing +multiprocessing.freeze_support() + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.intercept import intercept_build_main +sys.exit(intercept_build_main(this_dir)) diff --git a/tools/scan-build-py/bin/intercept-c++ b/tools/scan-build-py/bin/intercept-c++ new file mode 100644 index 000000000000..fc422287f84b --- /dev/null +++ b/tools/scan-build-py/bin/intercept-c++ @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.intercept import intercept_build_wrapper +sys.exit(intercept_build_wrapper(True)) diff --git a/tools/scan-build-py/bin/intercept-cc b/tools/scan-build-py/bin/intercept-cc new file mode 100644 index 000000000000..69d57aaae107 --- /dev/null +++ b/tools/scan-build-py/bin/intercept-cc @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.intercept import intercept_build_wrapper +sys.exit(intercept_build_wrapper(False)) diff --git a/tools/scan-build-py/bin/scan-build b/tools/scan-build-py/bin/scan-build new file mode 100644 index 000000000000..601fe89fc30d --- /dev/null +++ b/tools/scan-build-py/bin/scan-build @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import multiprocessing +multiprocessing.freeze_support() + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.analyze import analyze_build_main +sys.exit(analyze_build_main(this_dir, True)) diff --git a/tools/scan-build-py/libear/__init__.py b/tools/scan-build-py/libear/__init__.py new file mode 100644 index 000000000000..3e1c13cf2bfe --- /dev/null +++ b/tools/scan-build-py/libear/__init__.py @@ -0,0 +1,260 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +""" This module compiles the intercept library. """ + +import sys +import os +import os.path +import re +import tempfile +import shutil +import contextlib +import logging + +__all__ = ['build_libear'] + + +def build_libear(compiler, dst_dir): + """ Returns the full path to the 'libear' library. """ + + try: + src_dir = os.path.dirname(os.path.realpath(__file__)) + toolset = make_toolset(src_dir) + toolset.set_compiler(compiler) + toolset.set_language_standard('c99') + toolset.add_definitions(['-D_GNU_SOURCE']) + + configure = do_configure(toolset) + configure.check_function_exists('execve', 'HAVE_EXECVE') + configure.check_function_exists('execv', 'HAVE_EXECV') + configure.check_function_exists('execvpe', 'HAVE_EXECVPE') + configure.check_function_exists('execvp', 'HAVE_EXECVP') + configure.check_function_exists('execvP', 'HAVE_EXECVP2') + configure.check_function_exists('exect', 'HAVE_EXECT') + configure.check_function_exists('execl', 'HAVE_EXECL') + configure.check_function_exists('execlp', 'HAVE_EXECLP') + configure.check_function_exists('execle', 'HAVE_EXECLE') + configure.check_function_exists('posix_spawn', 'HAVE_POSIX_SPAWN') + configure.check_function_exists('posix_spawnp', 'HAVE_POSIX_SPAWNP') + configure.check_symbol_exists('_NSGetEnviron', 'crt_externs.h', + 'HAVE_NSGETENVIRON') + configure.write_by_template( + os.path.join(src_dir, 'config.h.in'), + os.path.join(dst_dir, 'config.h')) + + target = create_shared_library('ear', toolset) + target.add_include(dst_dir) + target.add_sources('ear.c') + target.link_against(toolset.dl_libraries()) + target.link_against(['pthread']) + target.build_release(dst_dir) + + return os.path.join(dst_dir, target.name) + + except Exception: + logging.info("Could not build interception library.", exc_info=True) + return None + + +def execute(cmd, *args, **kwargs): + """ Make subprocess execution silent. """ + + import subprocess + kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT}) + return subprocess.check_call(cmd, *args, **kwargs) + + +@contextlib.contextmanager +def TemporaryDirectory(**kwargs): + name = tempfile.mkdtemp(**kwargs) + try: + yield name + finally: + shutil.rmtree(name) + + +class Toolset(object): + """ Abstract class to represent different toolset. """ + + def __init__(self, src_dir): + self.src_dir = src_dir + self.compiler = None + self.c_flags = [] + + def set_compiler(self, compiler): + """ part of public interface """ + self.compiler = compiler + + def set_language_standard(self, standard): + """ part of public interface """ + self.c_flags.append('-std=' + standard) + + def add_definitions(self, defines): + """ part of public interface """ + self.c_flags.extend(defines) + + def dl_libraries(self): + raise NotImplementedError() + + def shared_library_name(self, name): + raise NotImplementedError() + + def shared_library_c_flags(self, release): + extra = ['-DNDEBUG', '-O3'] if release else [] + return extra + ['-fPIC'] + self.c_flags + + def shared_library_ld_flags(self, release, name): + raise NotImplementedError() + + +class DarwinToolset(Toolset): + def __init__(self, src_dir): + Toolset.__init__(self, src_dir) + + def dl_libraries(self): + return [] + + def shared_library_name(self, name): + return 'lib' + name + '.dylib' + + def shared_library_ld_flags(self, release, name): + extra = ['-dead_strip'] if release else [] + return extra + ['-dynamiclib', '-install_name', '@rpath/' + name] + + +class UnixToolset(Toolset): + def __init__(self, src_dir): + Toolset.__init__(self, src_dir) + + def dl_libraries(self): + return [] + + def shared_library_name(self, name): + return 'lib' + name + '.so' + + def shared_library_ld_flags(self, release, name): + extra = [] if release else [] + return extra + ['-shared', '-Wl,-soname,' + name] + + +class LinuxToolset(UnixToolset): + def __init__(self, src_dir): + UnixToolset.__init__(self, src_dir) + + def dl_libraries(self): + return ['dl'] + + +def make_toolset(src_dir): + platform = sys.platform + if platform in {'win32', 'cygwin'}: + raise RuntimeError('not implemented on this platform') + elif platform == 'darwin': + return DarwinToolset(src_dir) + elif platform in {'linux', 'linux2'}: + return LinuxToolset(src_dir) + else: + return UnixToolset(src_dir) + + +class Configure(object): + def __init__(self, toolset): + self.ctx = toolset + self.results = {'APPLE': sys.platform == 'darwin'} + + def _try_to_compile_and_link(self, source): + try: + with TemporaryDirectory() as work_dir: + src_file = 'check.c' + with open(os.path.join(work_dir, src_file), 'w') as handle: + handle.write(source) + + execute([self.ctx.compiler, src_file] + self.ctx.c_flags, + cwd=work_dir) + return True + except Exception: + return False + + def check_function_exists(self, function, name): + template = "int FUNCTION(); int main() { return FUNCTION(); }" + source = template.replace("FUNCTION", function) + + logging.debug('Checking function %s', function) + found = self._try_to_compile_and_link(source) + logging.debug('Checking function %s -- %s', function, + 'found' if found else 'not found') + self.results.update({name: found}) + + def check_symbol_exists(self, symbol, include, name): + template = """#include <INCLUDE> + int main() { return ((int*)(&SYMBOL))[0]; }""" + source = template.replace('INCLUDE', include).replace("SYMBOL", symbol) + + logging.debug('Checking symbol %s', symbol) + found = self._try_to_compile_and_link(source) + logging.debug('Checking symbol %s -- %s', symbol, + 'found' if found else 'not found') + self.results.update({name: found}) + + def write_by_template(self, template, output): + def transform(line, definitions): + + pattern = re.compile(r'^#cmakedefine\s+(\S+)') + m = pattern.match(line) + if m: + key = m.group(1) + if key not in definitions or not definitions[key]: + return '/* #undef {} */\n'.format(key) + else: + return '#define {}\n'.format(key) + return line + + with open(template, 'r') as src_handle: + logging.debug('Writing config to %s', output) + with open(output, 'w') as dst_handle: + for line in src_handle: + dst_handle.write(transform(line, self.results)) + + +def do_configure(toolset): + return Configure(toolset) + + +class SharedLibrary(object): + def __init__(self, name, toolset): + self.name = toolset.shared_library_name(name) + self.ctx = toolset + self.inc = [] + self.src = [] + self.lib = [] + + def add_include(self, directory): + self.inc.extend(['-I', directory]) + + def add_sources(self, source): + self.src.append(source) + + def link_against(self, libraries): + self.lib.extend(['-l' + lib for lib in libraries]) + + def build_release(self, directory): + for src in self.src: + logging.debug('Compiling %s', src) + execute( + [self.ctx.compiler, '-c', os.path.join(self.ctx.src_dir, src), + '-o', src + '.o'] + self.inc + + self.ctx.shared_library_c_flags(True), + cwd=directory) + logging.debug('Linking %s', self.name) + execute( + [self.ctx.compiler] + [src + '.o' for src in self.src] + + ['-o', self.name] + self.lib + + self.ctx.shared_library_ld_flags(True, self.name), + cwd=directory) + + +def create_shared_library(name, toolset): + return SharedLibrary(name, toolset) diff --git a/tools/scan-build-py/libear/config.h.in b/tools/scan-build-py/libear/config.h.in new file mode 100644 index 000000000000..6643d8995c88 --- /dev/null +++ b/tools/scan-build-py/libear/config.h.in @@ -0,0 +1,23 @@ +/* -*- coding: utf-8 -*- +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +*/ + +#pragma once + +#cmakedefine HAVE_EXECVE +#cmakedefine HAVE_EXECV +#cmakedefine HAVE_EXECVPE +#cmakedefine HAVE_EXECVP +#cmakedefine HAVE_EXECVP2 +#cmakedefine HAVE_EXECT +#cmakedefine HAVE_EXECL +#cmakedefine HAVE_EXECLP +#cmakedefine HAVE_EXECLE +#cmakedefine HAVE_POSIX_SPAWN +#cmakedefine HAVE_POSIX_SPAWNP +#cmakedefine HAVE_NSGETENVIRON + +#cmakedefine APPLE diff --git a/tools/scan-build-py/libear/ear.c b/tools/scan-build-py/libear/ear.c new file mode 100644 index 000000000000..0e7093af75a5 --- /dev/null +++ b/tools/scan-build-py/libear/ear.c @@ -0,0 +1,605 @@ +/* -*- coding: utf-8 -*- +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +*/ + +/** + * This file implements a shared library. This library can be pre-loaded by + * the dynamic linker of the Operating System (OS). It implements a few function + * related to process creation. By pre-load this library the executed process + * uses these functions instead of those from the standard library. + * + * The idea here is to inject a logic before call the real methods. The logic is + * to dump the call into a file. To call the real method this library is doing + * the job of the dynamic linker. + * + * The only input for the log writing is about the destination directory. + * This is passed as environment variable. + */ + +#include "config.h" + +#include <stddef.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <dlfcn.h> +#include <pthread.h> + +#if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP +#include <spawn.h> +#endif + +#if defined HAVE_NSGETENVIRON +# include <crt_externs.h> +#else +extern char **environ; +#endif + +#define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR" +#ifdef APPLE +# define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE" +# define ENV_PRELOAD "DYLD_INSERT_LIBRARIES" +# define ENV_SIZE 3 +#else +# define ENV_PRELOAD "LD_PRELOAD" +# define ENV_SIZE 2 +#endif + +#define DLSYM(TYPE_, VAR_, SYMBOL_) \ + union { \ + void *from; \ + TYPE_ to; \ + } cast; \ + if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) { \ + perror("bear: dlsym"); \ + exit(EXIT_FAILURE); \ + } \ + TYPE_ const VAR_ = cast.to; + + +typedef char const * bear_env_t[ENV_SIZE]; + +static int bear_capture_env_t(bear_env_t *env); +static int bear_reset_env_t(bear_env_t *env); +static void bear_release_env_t(bear_env_t *env); +static char const **bear_update_environment(char *const envp[], bear_env_t *env); +static char const **bear_update_environ(char const **in, char const *key, char const *value); +static char **bear_get_environment(); +static void bear_report_call(char const *fun, char const *const argv[]); +static char const **bear_strings_build(char const *arg, va_list *ap); +static char const **bear_strings_copy(char const **const in); +static char const **bear_strings_append(char const **in, char const *e); +static size_t bear_strings_length(char const *const *in); +static void bear_strings_release(char const **); + + +static bear_env_t env_names = + { ENV_OUTPUT + , ENV_PRELOAD +#ifdef ENV_FLAT + , ENV_FLAT +#endif + }; + +static bear_env_t initial_env = + { 0 + , 0 +#ifdef ENV_FLAT + , 0 +#endif + }; + +static int initialized = 0; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +static void on_load(void) __attribute__((constructor)); +static void on_unload(void) __attribute__((destructor)); + + +#ifdef HAVE_EXECVE +static int call_execve(const char *path, char *const argv[], + char *const envp[]); +#endif +#ifdef HAVE_EXECVP +static int call_execvp(const char *file, char *const argv[]); +#endif +#ifdef HAVE_EXECVPE +static int call_execvpe(const char *file, char *const argv[], + char *const envp[]); +#endif +#ifdef HAVE_EXECVP2 +static int call_execvP(const char *file, const char *search_path, + char *const argv[]); +#endif +#ifdef HAVE_EXECT +static int call_exect(const char *path, char *const argv[], + char *const envp[]); +#endif +#ifdef HAVE_POSIX_SPAWN +static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]); +#endif +#ifdef HAVE_POSIX_SPAWNP +static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]); +#endif + + +/* Initialization method to Captures the relevant environment variables. + */ + +static void on_load(void) { + pthread_mutex_lock(&mutex); + if (!initialized) + initialized = bear_capture_env_t(&initial_env); + pthread_mutex_unlock(&mutex); +} + +static void on_unload(void) { + pthread_mutex_lock(&mutex); + bear_release_env_t(&initial_env); + initialized = 0; + pthread_mutex_unlock(&mutex); +} + + +/* These are the methods we are try to hijack. + */ + +#ifdef HAVE_EXECVE +int execve(const char *path, char *const argv[], char *const envp[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execve(path, argv, envp); +} +#endif + +#ifdef HAVE_EXECV +#ifndef HAVE_EXECVE +#error can not implement execv without execve +#endif +int execv(const char *path, char *const argv[]) { + bear_report_call(__func__, (char const *const *)argv); + char * const * envp = bear_get_environment(); + return call_execve(path, argv, envp); +} +#endif + +#ifdef HAVE_EXECVPE +int execvpe(const char *file, char *const argv[], char *const envp[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execvpe(file, argv, envp); +} +#endif + +#ifdef HAVE_EXECVP +int execvp(const char *file, char *const argv[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execvp(file, argv); +} +#endif + +#ifdef HAVE_EXECVP2 +int execvP(const char *file, const char *search_path, char *const argv[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execvP(file, search_path, argv); +} +#endif + +#ifdef HAVE_EXECT +int exect(const char *path, char *const argv[], char *const envp[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_exect(path, argv, envp); +} +#endif + +#ifdef HAVE_EXECL +# ifndef HAVE_EXECVE +# error can not implement execl without execve +# endif +int execl(const char *path, const char *arg, ...) { + va_list args; + va_start(args, arg); + char const **argv = bear_strings_build(arg, &args); + va_end(args); + + bear_report_call(__func__, (char const *const *)argv); + char * const * envp = bear_get_environment(); + int const result = call_execve(path, (char *const *)argv, envp); + + bear_strings_release(argv); + return result; +} +#endif + +#ifdef HAVE_EXECLP +# ifndef HAVE_EXECVP +# error can not implement execlp without execvp +# endif +int execlp(const char *file, const char *arg, ...) { + va_list args; + va_start(args, arg); + char const **argv = bear_strings_build(arg, &args); + va_end(args); + + bear_report_call(__func__, (char const *const *)argv); + int const result = call_execvp(file, (char *const *)argv); + + bear_strings_release(argv); + return result; +} +#endif + +#ifdef HAVE_EXECLE +# ifndef HAVE_EXECVE +# error can not implement execle without execve +# endif +// int execle(const char *path, const char *arg, ..., char * const envp[]); +int execle(const char *path, const char *arg, ...) { + va_list args; + va_start(args, arg); + char const **argv = bear_strings_build(arg, &args); + char const **envp = va_arg(args, char const **); + va_end(args); + + bear_report_call(__func__, (char const *const *)argv); + int const result = + call_execve(path, (char *const *)argv, (char *const *)envp); + + bear_strings_release(argv); + return result; +} +#endif + +#ifdef HAVE_POSIX_SPAWN +int posix_spawn(pid_t *restrict pid, const char *restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], char *const envp[restrict]) { + bear_report_call(__func__, (char const *const *)argv); + return call_posix_spawn(pid, path, file_actions, attrp, argv, envp); +} +#endif + +#ifdef HAVE_POSIX_SPAWNP +int posix_spawnp(pid_t *restrict pid, const char *restrict file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], char *const envp[restrict]) { + bear_report_call(__func__, (char const *const *)argv); + return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp); +} +#endif + +/* These are the methods which forward the call to the standard implementation. + */ + +#ifdef HAVE_EXECVE +static int call_execve(const char *path, char *const argv[], + char *const envp[]) { + typedef int (*func)(const char *, char *const *, char *const *); + + DLSYM(func, fp, "execve"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = (*fp)(path, argv, (char *const *)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_EXECVPE +static int call_execvpe(const char *file, char *const argv[], + char *const envp[]) { + typedef int (*func)(const char *, char *const *, char *const *); + + DLSYM(func, fp, "execvpe"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = (*fp)(file, argv, (char *const *)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_EXECVP +static int call_execvp(const char *file, char *const argv[]) { + typedef int (*func)(const char *file, char *const argv[]); + + DLSYM(func, fp, "execvp"); + + bear_env_t current_env; + bear_capture_env_t(¤t_env); + bear_reset_env_t(&initial_env); + int const result = (*fp)(file, argv); + bear_reset_env_t(¤t_env); + bear_release_env_t(¤t_env); + + return result; +} +#endif + +#ifdef HAVE_EXECVP2 +static int call_execvP(const char *file, const char *search_path, + char *const argv[]) { + typedef int (*func)(const char *, const char *, char *const *); + + DLSYM(func, fp, "execvP"); + + bear_env_t current_env; + bear_capture_env_t(¤t_env); + bear_reset_env_t(&initial_env); + int const result = (*fp)(file, search_path, argv); + bear_reset_env_t(¤t_env); + bear_release_env_t(¤t_env); + + return result; +} +#endif + +#ifdef HAVE_EXECT +static int call_exect(const char *path, char *const argv[], + char *const envp[]) { + typedef int (*func)(const char *, char *const *, char *const *); + + DLSYM(func, fp, "exect"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = (*fp)(path, argv, (char *const *)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_POSIX_SPAWN +static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]) { + typedef int (*func)(pid_t *restrict, const char *restrict, + const posix_spawn_file_actions_t *, + const posix_spawnattr_t *restrict, + char *const *restrict, char *const *restrict); + + DLSYM(func, fp, "posix_spawn"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = + (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_POSIX_SPAWNP +static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]) { + typedef int (*func)(pid_t *restrict, const char *restrict, + const posix_spawn_file_actions_t *, + const posix_spawnattr_t *restrict, + char *const *restrict, char *const *restrict); + + DLSYM(func, fp, "posix_spawnp"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = + (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +/* this method is to write log about the process creation. */ + +static void bear_report_call(char const *fun, char const *const argv[]) { + static int const GS = 0x1d; + static int const RS = 0x1e; + static int const US = 0x1f; + + if (!initialized) + return; + + pthread_mutex_lock(&mutex); + const char *cwd = getcwd(NULL, 0); + if (0 == cwd) { + perror("bear: getcwd"); + exit(EXIT_FAILURE); + } + char const * const out_dir = initial_env[0]; + size_t const path_max_length = strlen(out_dir) + 32; + char filename[path_max_length]; + if (-1 == snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) { + perror("bear: snprintf"); + exit(EXIT_FAILURE); + } + FILE * fd = fopen(filename, "a+"); + if (0 == fd) { + perror("bear: fopen"); + exit(EXIT_FAILURE); + } + fprintf(fd, "%d%c", getpid(), RS); + fprintf(fd, "%d%c", getppid(), RS); + fprintf(fd, "%s%c", fun, RS); + fprintf(fd, "%s%c", cwd, RS); + size_t const argc = bear_strings_length(argv); + for (size_t it = 0; it < argc; ++it) { + fprintf(fd, "%s%c", argv[it], US); + } + fprintf(fd, "%c", GS); + if (fclose(fd)) { + perror("bear: fclose"); + exit(EXIT_FAILURE); + } + free((void *)cwd); + pthread_mutex_unlock(&mutex); +} + +/* update environment assure that chilren processes will copy the desired + * behaviour */ + +static int bear_capture_env_t(bear_env_t *env) { + int status = 1; + for (size_t it = 0; it < ENV_SIZE; ++it) { + char const * const env_value = getenv(env_names[it]); + char const * const env_copy = (env_value) ? strdup(env_value) : env_value; + (*env)[it] = env_copy; + status &= (env_copy) ? 1 : 0; + } + return status; +} + +static int bear_reset_env_t(bear_env_t *env) { + int status = 1; + for (size_t it = 0; it < ENV_SIZE; ++it) { + if ((*env)[it]) { + setenv(env_names[it], (*env)[it], 1); + } else { + unsetenv(env_names[it]); + } + } + return status; +} + +static void bear_release_env_t(bear_env_t *env) { + for (size_t it = 0; it < ENV_SIZE; ++it) { + free((void *)(*env)[it]); + (*env)[it] = 0; + } +} + +static char const **bear_update_environment(char *const envp[], bear_env_t *env) { + char const **result = bear_strings_copy((char const **)envp); + for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it) + result = bear_update_environ(result, env_names[it], (*env)[it]); + return result; +} + +static char const **bear_update_environ(char const *envs[], char const *key, char const * const value) { + // find the key if it's there + size_t const key_length = strlen(key); + char const **it = envs; + for (; (it) && (*it); ++it) { + if ((0 == strncmp(*it, key, key_length)) && + (strlen(*it) > key_length) && ('=' == (*it)[key_length])) + break; + } + // allocate a environment entry + size_t const value_length = strlen(value); + size_t const env_length = key_length + value_length + 2; + char *env = malloc(env_length); + if (0 == env) { + perror("bear: malloc [in env_update]"); + exit(EXIT_FAILURE); + } + if (-1 == snprintf(env, env_length, "%s=%s", key, value)) { + perror("bear: snprintf"); + exit(EXIT_FAILURE); + } + // replace or append the environment entry + if (it && *it) { + free((void *)*it); + *it = env; + return envs; + } + return bear_strings_append(envs, env); +} + +static char **bear_get_environment() { +#if defined HAVE_NSGETENVIRON + return *_NSGetEnviron(); +#else + return environ; +#endif +} + +/* util methods to deal with string arrays. environment and process arguments + * are both represented as string arrays. */ + +static char const **bear_strings_build(char const *const arg, va_list *args) { + char const **result = 0; + size_t size = 0; + for (char const *it = arg; it; it = va_arg(*args, char const *)) { + result = realloc(result, (size + 1) * sizeof(char const *)); + if (0 == result) { + perror("bear: realloc"); + exit(EXIT_FAILURE); + } + char const *copy = strdup(it); + if (0 == copy) { + perror("bear: strdup"); + exit(EXIT_FAILURE); + } + result[size++] = copy; + } + result = realloc(result, (size + 1) * sizeof(char const *)); + if (0 == result) { + perror("bear: realloc"); + exit(EXIT_FAILURE); + } + result[size++] = 0; + + return result; +} + +static char const **bear_strings_copy(char const **const in) { + size_t const size = bear_strings_length(in); + + char const **const result = malloc((size + 1) * sizeof(char const *)); + if (0 == result) { + perror("bear: malloc"); + exit(EXIT_FAILURE); + } + + char const **out_it = result; + for (char const *const *in_it = in; (in_it) && (*in_it); + ++in_it, ++out_it) { + *out_it = strdup(*in_it); + if (0 == *out_it) { + perror("bear: strdup"); + exit(EXIT_FAILURE); + } + } + *out_it = 0; + return result; +} + +static char const **bear_strings_append(char const **const in, + char const *const e) { + size_t size = bear_strings_length(in); + char const **result = realloc(in, (size + 2) * sizeof(char const *)); + if (0 == result) { + perror("bear: realloc"); + exit(EXIT_FAILURE); + } + result[size++] = e; + result[size++] = 0; + return result; +} + +static size_t bear_strings_length(char const *const *const in) { + size_t result = 0; + for (char const *const *it = in; (it) && (*it); ++it) + ++result; + return result; +} + +static void bear_strings_release(char const **in) { + for (char const *const *it = in; (it) && (*it); ++it) { + free((void *)*it); + } + free((void *)in); +} diff --git a/tools/scan-build-py/libscanbuild/__init__.py b/tools/scan-build-py/libscanbuild/__init__.py new file mode 100644 index 000000000000..c020b4e4345d --- /dev/null +++ b/tools/scan-build-py/libscanbuild/__init__.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +""" +This module responsible to run the Clang static analyzer against any build +and generate reports. +""" + + +def duplicate_check(method): + """ Predicate to detect duplicated entries. + + Unique hash method can be use to detect duplicates. Entries are + represented as dictionaries, which has no default hash method. + This implementation uses a set datatype to store the unique hash values. + + This method returns a method which can detect the duplicate values. """ + + def predicate(entry): + entry_hash = predicate.unique(entry) + if entry_hash not in predicate.state: + predicate.state.add(entry_hash) + return False + return True + + predicate.unique = method + predicate.state = set() + return predicate + + +def tempdir(): + """ Return the default temorary directory. """ + + from os import getenv + return getenv('TMPDIR', getenv('TEMP', getenv('TMP', '/tmp'))) + + +def initialize_logging(verbose_level): + """ Output content controlled by the verbosity level. """ + + import sys + import os.path + import logging + level = logging.WARNING - min(logging.WARNING, (10 * verbose_level)) + + if verbose_level <= 3: + fmt_string = '{0}: %(levelname)s: %(message)s' + else: + fmt_string = '{0}: %(levelname)s: %(funcName)s: %(message)s' + + program = os.path.basename(sys.argv[0]) + logging.basicConfig(format=fmt_string.format(program), level=level) + + +def command_entry_point(function): + """ Decorator for command entry points. """ + + import functools + import logging + + @functools.wraps(function) + def wrapper(*args, **kwargs): + + exit_code = 127 + try: + exit_code = function(*args, **kwargs) + except KeyboardInterrupt: + logging.warning('Keyboard interupt') + except Exception: + logging.exception('Internal error.') + if logging.getLogger().isEnabledFor(logging.DEBUG): + logging.error("Please report this bug and attach the output " + "to the bug report") + else: + logging.error("Please run this command again and turn on " + "verbose mode (add '-vvv' as argument).") + finally: + return exit_code + + return wrapper diff --git a/tools/scan-build-py/libscanbuild/analyze.py b/tools/scan-build-py/libscanbuild/analyze.py new file mode 100644 index 000000000000..0d3547befeef --- /dev/null +++ b/tools/scan-build-py/libscanbuild/analyze.py @@ -0,0 +1,502 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +""" This module implements the 'scan-build' command API. + +To run the static analyzer against a build is done in multiple steps: + + -- Intercept: capture the compilation command during the build, + -- Analyze: run the analyzer against the captured commands, + -- Report: create a cover report from the analyzer outputs. """ + +import sys +import re +import os +import os.path +import json +import argparse +import logging +import subprocess +import multiprocessing +from libscanbuild import initialize_logging, tempdir, command_entry_point +from libscanbuild.runner import run +from libscanbuild.intercept import capture +from libscanbuild.report import report_directory, document +from libscanbuild.clang import get_checkers +from libscanbuild.runner import action_check +from libscanbuild.command import classify_parameters, classify_source + +__all__ = ['analyze_build_main', 'analyze_build_wrapper'] + +COMPILER_WRAPPER_CC = 'analyze-cc' +COMPILER_WRAPPER_CXX = 'analyze-c++' + + +@command_entry_point +def analyze_build_main(bin_dir, from_build_command): + """ Entry point for 'analyze-build' and 'scan-build'. """ + + parser = create_parser(from_build_command) + args = parser.parse_args() + validate(parser, args, from_build_command) + + # setup logging + initialize_logging(args.verbose) + logging.debug('Parsed arguments: %s', args) + + with report_directory(args.output, args.keep_empty) as target_dir: + if not from_build_command: + # run analyzer only and generate cover report + run_analyzer(args, target_dir) + number_of_bugs = document(args, target_dir, True) + return number_of_bugs if args.status_bugs else 0 + elif args.intercept_first: + # run build command and capture compiler executions + exit_code = capture(args, bin_dir) + # next step to run the analyzer against the captured commands + if need_analyzer(args.build): + run_analyzer(args, target_dir) + # cover report generation and bug counting + number_of_bugs = document(args, target_dir, True) + # remove the compilation database when it was not requested + if os.path.exists(args.cdb): + os.unlink(args.cdb) + # set exit status as it was requested + return number_of_bugs if args.status_bugs else exit_code + else: + return exit_code + else: + # run the build command with compiler wrappers which + # execute the analyzer too. (interposition) + environment = setup_environment(args, target_dir, bin_dir) + logging.debug('run build in environment: %s', environment) + exit_code = subprocess.call(args.build, env=environment) + logging.debug('build finished with exit code: %d', exit_code) + # cover report generation and bug counting + number_of_bugs = document(args, target_dir, False) + # set exit status as it was requested + return number_of_bugs if args.status_bugs else exit_code + + +def need_analyzer(args): + """ Check the intent of the build command. + + When static analyzer run against project configure step, it should be + silent and no need to run the analyzer or generate report. + + To run `scan-build` against the configure step might be neccessary, + when compiler wrappers are used. That's the moment when build setup + check the compiler and capture the location for the build process. """ + + return len(args) and not re.search('configure|autogen', args[0]) + + +def run_analyzer(args, output_dir): + """ Runs the analyzer against the given compilation database. """ + + def exclude(filename): + """ Return true when any excluded directory prefix the filename. """ + return any(re.match(r'^' + directory, filename) + for directory in args.excludes) + + consts = { + 'clang': args.clang, + 'output_dir': output_dir, + 'output_format': args.output_format, + 'output_failures': args.output_failures, + 'direct_args': analyzer_params(args) + } + + logging.debug('run analyzer against compilation database') + with open(args.cdb, 'r') as handle: + generator = (dict(cmd, **consts) + for cmd in json.load(handle) if not exclude(cmd['file'])) + # when verbose output requested execute sequentially + pool = multiprocessing.Pool(1 if args.verbose > 2 else None) + for current in pool.imap_unordered(run, generator): + if current is not None: + # display error message from the static analyzer + for line in current['error_output']: + logging.info(line.rstrip()) + pool.close() + pool.join() + + +def setup_environment(args, destination, bin_dir): + """ Set up environment for build command to interpose compiler wrapper. """ + + environment = dict(os.environ) + environment.update({ + 'CC': os.path.join(bin_dir, COMPILER_WRAPPER_CC), + 'CXX': os.path.join(bin_dir, COMPILER_WRAPPER_CXX), + 'ANALYZE_BUILD_CC': args.cc, + 'ANALYZE_BUILD_CXX': args.cxx, + 'ANALYZE_BUILD_CLANG': args.clang if need_analyzer(args.build) else '', + 'ANALYZE_BUILD_VERBOSE': 'DEBUG' if args.verbose > 2 else 'WARNING', + 'ANALYZE_BUILD_REPORT_DIR': destination, + 'ANALYZE_BUILD_REPORT_FORMAT': args.output_format, + 'ANALYZE_BUILD_REPORT_FAILURES': 'yes' if args.output_failures else '', + 'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)) + }) + return environment + + +def analyze_build_wrapper(cplusplus): + """ Entry point for `analyze-cc` and `analyze-c++` compiler wrappers. """ + + # initialize wrapper logging + logging.basicConfig(format='analyze: %(levelname)s: %(message)s', + level=os.getenv('ANALYZE_BUILD_VERBOSE', 'INFO')) + # execute with real compiler + compiler = os.getenv('ANALYZE_BUILD_CXX', 'c++') if cplusplus \ + else os.getenv('ANALYZE_BUILD_CC', 'cc') + compilation = [compiler] + sys.argv[1:] + logging.info('execute compiler: %s', compilation) + result = subprocess.call(compilation) + # exit when it fails, ... + if result or not os.getenv('ANALYZE_BUILD_CLANG'): + return result + # ... and run the analyzer if all went well. + try: + # collect the needed parameters from environment, crash when missing + consts = { + 'clang': os.getenv('ANALYZE_BUILD_CLANG'), + 'output_dir': os.getenv('ANALYZE_BUILD_REPORT_DIR'), + 'output_format': os.getenv('ANALYZE_BUILD_REPORT_FORMAT'), + 'output_failures': os.getenv('ANALYZE_BUILD_REPORT_FAILURES'), + 'direct_args': os.getenv('ANALYZE_BUILD_PARAMETERS', + '').split(' '), + 'directory': os.getcwd(), + } + # get relevant parameters from command line arguments + args = classify_parameters(sys.argv) + filenames = args.pop('files', []) + for filename in (name for name in filenames if classify_source(name)): + parameters = dict(args, file=filename, **consts) + logging.debug('analyzer parameters %s', parameters) + current = action_check(parameters) + # display error message from the static analyzer + if current is not None: + for line in current['error_output']: + logging.info(line.rstrip()) + except Exception: + logging.exception("run analyzer inside compiler wrapper failed.") + return 0 + + +def analyzer_params(args): + """ A group of command line arguments can mapped to command + line arguments of the analyzer. This method generates those. """ + + def prefix_with(constant, pieces): + """ From a sequence create another sequence where every second element + is from the original sequence and the odd elements are the prefix. + + eg.: prefix_with(0, [1,2,3]) creates [0, 1, 0, 2, 0, 3] """ + + return [elem for piece in pieces for elem in [constant, piece]] + + result = [] + + if args.store_model: + result.append('-analyzer-store={0}'.format(args.store_model)) + if args.constraints_model: + result.append( + '-analyzer-constraints={0}'.format(args.constraints_model)) + if args.internal_stats: + result.append('-analyzer-stats') + if args.analyze_headers: + result.append('-analyzer-opt-analyze-headers') + if args.stats: + result.append('-analyzer-checker=debug.Stats') + if args.maxloop: + result.extend(['-analyzer-max-loop', str(args.maxloop)]) + if args.output_format: + result.append('-analyzer-output={0}'.format(args.output_format)) + if args.analyzer_config: + result.append(args.analyzer_config) + if args.verbose >= 4: + result.append('-analyzer-display-progress') + if args.plugins: + result.extend(prefix_with('-load', args.plugins)) + if args.enable_checker: + checkers = ','.join(args.enable_checker) + result.extend(['-analyzer-checker', checkers]) + if args.disable_checker: + checkers = ','.join(args.disable_checker) + result.extend(['-analyzer-disable-checker', checkers]) + if os.getenv('UBIVIZ'): + result.append('-analyzer-viz-egraph-ubigraph') + + return prefix_with('-Xclang', result) + + +def print_active_checkers(checkers): + """ Print active checkers to stdout. """ + + for name in sorted(name for name, (_, active) in checkers.items() + if active): + print(name) + + +def print_checkers(checkers): + """ Print verbose checker help to stdout. """ + + print('') + print('available checkers:') + print('') + for name in sorted(checkers.keys()): + description, active = checkers[name] + prefix = '+' if active else ' ' + if len(name) > 30: + print(' {0} {1}'.format(prefix, name)) + print(' ' * 35 + description) + else: + print(' {0} {1: <30} {2}'.format(prefix, name, description)) + print('') + print('NOTE: "+" indicates that an analysis is enabled by default.') + print('') + + +def validate(parser, args, from_build_command): + """ Validation done by the parser itself, but semantic check still + needs to be done. This method is doing that. """ + + if args.help_checkers_verbose: + print_checkers(get_checkers(args.clang, args.plugins)) + parser.exit() + elif args.help_checkers: + print_active_checkers(get_checkers(args.clang, args.plugins)) + parser.exit() + + if from_build_command and not args.build: + parser.error('missing build command') + + +def create_parser(from_build_command): + """ Command line argument parser factory method. """ + + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + '--verbose', '-v', + action='count', + default=0, + help="""Enable verbose output from '%(prog)s'. A second and third + flag increases verbosity.""") + parser.add_argument( + '--override-compiler', + action='store_true', + help="""Always resort to the compiler wrapper even when better + interposition methods are available.""") + parser.add_argument( + '--intercept-first', + action='store_true', + help="""Run the build commands only, build a compilation database, + then run the static analyzer afterwards. + Generally speaking it has better coverage on build commands. + With '--override-compiler' it use compiler wrapper, but does + not run the analyzer till the build is finished. """) + parser.add_argument( + '--cdb', + metavar='<file>', + default="compile_commands.json", + help="""The JSON compilation database.""") + + parser.add_argument( + '--output', '-o', + metavar='<path>', + default=tempdir(), + help="""Specifies the output directory for analyzer reports. + Subdirectory will be created if default directory is targeted. + """) + parser.add_argument( + '--status-bugs', + action='store_true', + help="""By default, the exit status of '%(prog)s' is the same as the + executed build command. Specifying this option causes the exit + status of '%(prog)s' to be non zero if it found potential bugs + and zero otherwise.""") + parser.add_argument( + '--html-title', + metavar='<title>', + help="""Specify the title used on generated HTML pages. + If not specified, a default title will be used.""") + parser.add_argument( + '--analyze-headers', + action='store_true', + help="""Also analyze functions in #included files. By default, such + functions are skipped unless they are called by functions + within the main source file.""") + format_group = parser.add_mutually_exclusive_group() + format_group.add_argument( + '--plist', '-plist', + dest='output_format', + const='plist', + default='html', + action='store_const', + help="""This option outputs the results as a set of .plist files.""") + format_group.add_argument( + '--plist-html', '-plist-html', + dest='output_format', + const='plist-html', + default='html', + action='store_const', + help="""This option outputs the results as a set of .html and .plist + files.""") + # TODO: implement '-view ' + + advanced = parser.add_argument_group('advanced options') + advanced.add_argument( + '--keep-empty', + action='store_true', + help="""Don't remove the build results directory even if no issues + were reported.""") + advanced.add_argument( + '--no-failure-reports', '-no-failure-reports', + dest='output_failures', + action='store_false', + help="""Do not create a 'failures' subdirectory that includes analyzer + crash reports and preprocessed source files.""") + advanced.add_argument( + '--stats', '-stats', + action='store_true', + help="""Generates visitation statistics for the project being analyzed. + """) + advanced.add_argument( + '--internal-stats', + action='store_true', + help="""Generate internal analyzer statistics.""") + advanced.add_argument( + '--maxloop', '-maxloop', + metavar='<loop count>', + type=int, + help="""Specifiy the number of times a block can be visited before + giving up. Increase for more comprehensive coverage at a cost + of speed.""") + advanced.add_argument( + '--store', '-store', + metavar='<model>', + dest='store_model', + choices=['region', 'basic'], + help="""Specify the store model used by the analyzer. + 'region' specifies a field- sensitive store model. + 'basic' which is far less precise but can more quickly + analyze code. 'basic' was the default store model for + checker-0.221 and earlier.""") + advanced.add_argument( + '--constraints', '-constraints', + metavar='<model>', + dest='constraints_model', + choices=['range', 'basic'], + help="""Specify the contraint engine used by the analyzer. Specifying + 'basic' uses a simpler, less powerful constraint model used by + checker-0.160 and earlier.""") + advanced.add_argument( + '--use-analyzer', + metavar='<path>', + dest='clang', + default='clang', + help="""'%(prog)s' uses the 'clang' executable relative to itself for + static analysis. One can override this behavior with this + option by using the 'clang' packaged with Xcode (on OS X) or + from the PATH.""") + advanced.add_argument( + '--use-cc', + metavar='<path>', + dest='cc', + default='cc', + help="""When '%(prog)s' analyzes a project by interposing a "fake + compiler", which executes a real compiler for compilation and + do other tasks (to run the static analyzer or just record the + compiler invocation). Because of this interposing, '%(prog)s' + does not know what compiler your project normally uses. + Instead, it simply overrides the CC environment variable, and + guesses your default compiler. + + If you need '%(prog)s' to use a specific compiler for + *compilation* then you can use this option to specify a path + to that compiler.""") + advanced.add_argument( + '--use-c++', + metavar='<path>', + dest='cxx', + default='c++', + help="""This is the same as "--use-cc" but for C++ code.""") + advanced.add_argument( + '--analyzer-config', '-analyzer-config', + metavar='<options>', + help="""Provide options to pass through to the analyzer's + -analyzer-config flag. Several options are separated with + comma: 'key1=val1,key2=val2' + + Available options: + stable-report-filename=true or false (default) + + Switch the page naming to: + report-<filename>-<function/method name>-<id>.html + instead of report-XXXXXX.html""") + advanced.add_argument( + '--exclude', + metavar='<directory>', + dest='excludes', + action='append', + default=[], + help="""Do not run static analyzer against files found in this + directory. (You can specify this option multiple times.) + Could be usefull when project contains 3rd party libraries. + The directory path shall be absolute path as file names in + the compilation database.""") + + plugins = parser.add_argument_group('checker options') + plugins.add_argument( + '--load-plugin', '-load-plugin', + metavar='<plugin library>', + dest='plugins', + action='append', + help="""Loading external checkers using the clang plugin interface.""") + plugins.add_argument( + '--enable-checker', '-enable-checker', + metavar='<checker name>', + action=AppendCommaSeparated, + help="""Enable specific checker.""") + plugins.add_argument( + '--disable-checker', '-disable-checker', + metavar='<checker name>', + action=AppendCommaSeparated, + help="""Disable specific checker.""") + plugins.add_argument( + '--help-checkers', + action='store_true', + help="""A default group of checkers is run unless explicitly disabled. + Exactly which checkers constitute the default group is a + function of the operating system in use. These can be printed + with this flag.""") + plugins.add_argument( + '--help-checkers-verbose', + action='store_true', + help="""Print all available checkers and mark the enabled ones.""") + + if from_build_command: + parser.add_argument( + dest='build', + nargs=argparse.REMAINDER, + help="""Command to run.""") + + return parser + + +class AppendCommaSeparated(argparse.Action): + """ argparse Action class to support multiple comma separated lists. """ + + def __call__(self, __parser, namespace, values, __option_string): + # getattr(obj, attr, default) does not really returns default but none + if getattr(namespace, self.dest, None) is None: + setattr(namespace, self.dest, []) + # once it's fixed we can use as expected + actual = getattr(namespace, self.dest) + actual.extend(values.split(',')) + setattr(namespace, self.dest, actual) diff --git a/tools/scan-build-py/libscanbuild/clang.py b/tools/scan-build-py/libscanbuild/clang.py new file mode 100644 index 000000000000..0c3454b16a76 --- /dev/null +++ b/tools/scan-build-py/libscanbuild/clang.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +""" This module is responsible for the Clang executable. + +Since Clang command line interface is so rich, but this project is using only +a subset of that, it makes sense to create a function specific wrapper. """ + +import re +import subprocess +import logging +from libscanbuild.shell import decode + +__all__ = ['get_version', 'get_arguments', 'get_checkers'] + + +def get_version(cmd): + """ Returns the compiler version as string. """ + + lines = subprocess.check_output([cmd, '-v'], stderr=subprocess.STDOUT) + return lines.decode('ascii').splitlines()[0] + + +def get_arguments(command, cwd): + """ Capture Clang invocation. + + This method returns the front-end invocation that would be executed as + a result of the given driver invocation. """ + + def lastline(stream): + last = None + for line in stream: + last = line + if last is None: + raise Exception("output not found") + return last + + cmd = command[:] + cmd.insert(1, '-###') + logging.debug('exec command in %s: %s', cwd, ' '.join(cmd)) + child = subprocess.Popen(cmd, + cwd=cwd, + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + line = lastline(child.stdout) + child.stdout.close() + child.wait() + if child.returncode == 0: + if re.search(r'clang(.*): error:', line): + raise Exception(line) + return decode(line) + else: + raise Exception(line) + + +def get_active_checkers(clang, plugins): + """ To get the default plugins we execute Clang to print how this + compilation would be called. + + For input file we specify stdin and pass only language information. """ + + def checkers(language): + """ Returns a list of active checkers for the given language. """ + + load = [elem + for plugin in plugins + for elem in ['-Xclang', '-load', '-Xclang', plugin]] + cmd = [clang, '--analyze'] + load + ['-x', language, '-'] + pattern = re.compile(r'^-analyzer-checker=(.*)$') + return [pattern.match(arg).group(1) + for arg in get_arguments(cmd, '.') if pattern.match(arg)] + + result = set() + for language in ['c', 'c++', 'objective-c', 'objective-c++']: + result.update(checkers(language)) + return result + + +def get_checkers(clang, plugins): + """ Get all the available checkers from default and from the plugins. + + clang -- the compiler we are using + plugins -- list of plugins which was requested by the user + + This method returns a dictionary of all available checkers and status. + + {<plugin name>: (<plugin description>, <is active by default>)} """ + + plugins = plugins if plugins else [] + + def parse_checkers(stream): + """ Parse clang -analyzer-checker-help output. + + Below the line 'CHECKERS:' are there the name description pairs. + Many of them are in one line, but some long named plugins has the + name and the description in separate lines. + + The plugin name is always prefixed with two space character. The + name contains no whitespaces. Then followed by newline (if it's + too long) or other space characters comes the description of the + plugin. The description ends with a newline character. """ + + # find checkers header + for line in stream: + if re.match(r'^CHECKERS:', line): + break + # find entries + state = None + for line in stream: + if state and not re.match(r'^\s\s\S', line): + yield (state, line.strip()) + state = None + elif re.match(r'^\s\s\S+$', line.rstrip()): + state = line.strip() + else: + pattern = re.compile(r'^\s\s(?P<key>\S*)\s*(?P<value>.*)') + match = pattern.match(line.rstrip()) + if match: + current = match.groupdict() + yield (current['key'], current['value']) + + def is_active(actives, entry): + """ Returns true if plugin name is matching the active plugin names. + + actives -- set of active plugin names (or prefixes). + entry -- the current plugin name to judge. + + The active plugin names are specific plugin names or prefix of some + names. One example for prefix, when it say 'unix' and it shall match + on 'unix.API', 'unix.Malloc' and 'unix.MallocSizeof'. """ + + return any(re.match(r'^' + a + r'(\.|$)', entry) for a in actives) + + actives = get_active_checkers(clang, plugins) + + load = [elem for plugin in plugins for elem in ['-load', plugin]] + cmd = [clang, '-cc1'] + load + ['-analyzer-checker-help'] + + logging.debug('exec command: %s', ' '.join(cmd)) + child = subprocess.Popen(cmd, + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + checkers = { + k: (v, is_active(actives, k)) + for k, v in parse_checkers(child.stdout) + } + child.stdout.close() + child.wait() + if child.returncode == 0 and len(checkers): + return checkers + else: + raise Exception('Could not query Clang for available checkers.') diff --git a/tools/scan-build-py/libscanbuild/command.py b/tools/scan-build-py/libscanbuild/command.py new file mode 100644 index 000000000000..69ca3393f955 --- /dev/null +++ b/tools/scan-build-py/libscanbuild/command.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +""" This module is responsible for to parse a compiler invocation. """ + +import re +import os + +__all__ = ['Action', 'classify_parameters', 'classify_source'] + + +class Action(object): + """ Enumeration class for compiler action. """ + + Link, Compile, Ignored = range(3) + + +def classify_parameters(command): + """ Parses the command line arguments of the given invocation. """ + + # result value of this method. + # some value are preset, some will be set only when found. + result = { + 'action': Action.Link, + 'files': [], + 'output': None, + 'compile_options': [], + 'c++': is_cplusplus_compiler(command[0]) + # archs_seen + # language + } + + # data structure to ignore compiler parameters. + # key: parameter name, value: number of parameters to ignore afterwards. + ignored = { + '-g': 0, + '-fsyntax-only': 0, + '-save-temps': 0, + '-install_name': 1, + '-exported_symbols_list': 1, + '-current_version': 1, + '-compatibility_version': 1, + '-init': 1, + '-e': 1, + '-seg1addr': 1, + '-bundle_loader': 1, + '-multiply_defined': 1, + '-sectorder': 3, + '--param': 1, + '--serialize-diagnostics': 1 + } + + args = iter(command[1:]) + for arg in args: + # compiler action parameters are the most important ones... + if arg in {'-E', '-S', '-cc1', '-M', '-MM', '-###'}: + result.update({'action': Action.Ignored}) + elif arg == '-c': + result.update({'action': max(result['action'], Action.Compile)}) + # arch flags are taken... + elif arg == '-arch': + archs = result.get('archs_seen', []) + result.update({'archs_seen': archs + [next(args)]}) + # explicit language option taken... + elif arg == '-x': + result.update({'language': next(args)}) + # output flag taken... + elif arg == '-o': + result.update({'output': next(args)}) + # warning disable options are taken... + elif re.match(r'^-Wno-', arg): + result['compile_options'].append(arg) + # warning options are ignored... + elif re.match(r'^-[mW].+', arg): + pass + # some preprocessor parameters are ignored... + elif arg in {'-MD', '-MMD', '-MG', '-MP'}: + pass + elif arg in {'-MF', '-MT', '-MQ'}: + next(args) + # linker options are ignored... + elif arg in {'-static', '-shared', '-s', '-rdynamic'} or \ + re.match(r'^-[lL].+', arg): + pass + elif arg in {'-l', '-L', '-u', '-z', '-T', '-Xlinker'}: + next(args) + # some other options are ignored... + elif arg in ignored.keys(): + for _ in range(ignored[arg]): + next(args) + # parameters which looks source file are taken... + elif re.match(r'^[^-].+', arg) and classify_source(arg): + result['files'].append(arg) + # and consider everything else as compile option. + else: + result['compile_options'].append(arg) + + return result + + +def classify_source(filename, cplusplus=False): + """ Return the language from file name extension. """ + + mapping = { + '.c': 'c++' if cplusplus else 'c', + '.i': 'c++-cpp-output' if cplusplus else 'c-cpp-output', + '.ii': 'c++-cpp-output', + '.m': 'objective-c', + '.mi': 'objective-c-cpp-output', + '.mm': 'objective-c++', + '.mii': 'objective-c++-cpp-output', + '.C': 'c++', + '.cc': 'c++', + '.CC': 'c++', + '.cp': 'c++', + '.cpp': 'c++', + '.cxx': 'c++', + '.c++': 'c++', + '.C++': 'c++', + '.txx': 'c++' + } + + __, extension = os.path.splitext(os.path.basename(filename)) + return mapping.get(extension) + + +def is_cplusplus_compiler(name): + """ Returns true when the compiler name refer to a C++ compiler. """ + + match = re.match(r'^([^/]*/)*(\w*-)*(\w+\+\+)(-(\d+(\.\d+){0,3}))?$', name) + return False if match is None else True diff --git a/tools/scan-build-py/libscanbuild/intercept.py b/tools/scan-build-py/libscanbuild/intercept.py new file mode 100644 index 000000000000..6062e2ea8ca9 --- /dev/null +++ b/tools/scan-build-py/libscanbuild/intercept.py @@ -0,0 +1,359 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +""" This module is responsible to capture the compiler invocation of any +build process. The result of that should be a compilation database. + +This implementation is using the LD_PRELOAD or DYLD_INSERT_LIBRARIES +mechanisms provided by the dynamic linker. The related library is implemented +in C language and can be found under 'libear' directory. + +The 'libear' library is capturing all child process creation and logging the +relevant information about it into separate files in a specified directory. +The parameter of this process is the output directory name, where the report +files shall be placed. This parameter is passed as an environment variable. + +The module also implements compiler wrappers to intercept the compiler calls. + +The module implements the build command execution and the post-processing of +the output files, which will condensates into a compilation database. """ + +import sys +import os +import os.path +import re +import itertools +import json +import glob +import argparse +import logging +import subprocess +from libear import build_libear, TemporaryDirectory +from libscanbuild import duplicate_check, tempdir, initialize_logging +from libscanbuild import command_entry_point +from libscanbuild.command import Action, classify_parameters +from libscanbuild.shell import encode, decode + +__all__ = ['capture', 'intercept_build_main', 'intercept_build_wrapper'] + +GS = chr(0x1d) +RS = chr(0x1e) +US = chr(0x1f) + +COMPILER_WRAPPER_CC = 'intercept-cc' +COMPILER_WRAPPER_CXX = 'intercept-c++' + + +@command_entry_point +def intercept_build_main(bin_dir): + """ Entry point for 'intercept-build' command. """ + + parser = create_parser() + args = parser.parse_args() + + initialize_logging(args.verbose) + logging.debug('Parsed arguments: %s', args) + + if not args.build: + parser.print_help() + return 0 + + return capture(args, bin_dir) + + +def capture(args, bin_dir): + """ The entry point of build command interception. """ + + def post_processing(commands): + """ To make a compilation database, it needs to filter out commands + which are not compiler calls. Needs to find the source file name + from the arguments. And do shell escaping on the command. + + To support incremental builds, it is desired to read elements from + an existing compilation database from a previous run. These elemets + shall be merged with the new elements. """ + + # create entries from the current run + current = itertools.chain.from_iterable( + # creates a sequence of entry generators from an exec, + # but filter out non compiler calls before. + (format_entry(x) for x in commands if is_compiler_call(x))) + # read entries from previous run + if 'append' in args and args.append and os.path.exists(args.cdb): + with open(args.cdb) as handle: + previous = iter(json.load(handle)) + else: + previous = iter([]) + # filter out duplicate entries from both + duplicate = duplicate_check(entry_hash) + return (entry for entry in itertools.chain(previous, current) + if os.path.exists(entry['file']) and not duplicate(entry)) + + with TemporaryDirectory(prefix='intercept-', dir=tempdir()) as tmp_dir: + # run the build command + environment = setup_environment(args, tmp_dir, bin_dir) + logging.debug('run build in environment: %s', environment) + exit_code = subprocess.call(args.build, env=environment) + logging.info('build finished with exit code: %d', exit_code) + # read the intercepted exec calls + commands = itertools.chain.from_iterable( + parse_exec_trace(os.path.join(tmp_dir, filename)) + for filename in sorted(glob.iglob(os.path.join(tmp_dir, '*.cmd')))) + # do post processing only if that was requested + if 'raw_entries' not in args or not args.raw_entries: + entries = post_processing(commands) + else: + entries = commands + # dump the compilation database + with open(args.cdb, 'w+') as handle: + json.dump(list(entries), handle, sort_keys=True, indent=4) + return exit_code + + +def setup_environment(args, destination, bin_dir): + """ Sets up the environment for the build command. + + It sets the required environment variables and execute the given command. + The exec calls will be logged by the 'libear' preloaded library or by the + 'wrapper' programs. """ + + c_compiler = args.cc if 'cc' in args else 'cc' + cxx_compiler = args.cxx if 'cxx' in args else 'c++' + + libear_path = None if args.override_compiler or is_preload_disabled( + sys.platform) else build_libear(c_compiler, destination) + + environment = dict(os.environ) + environment.update({'INTERCEPT_BUILD_TARGET_DIR': destination}) + + if not libear_path: + logging.debug('intercept gonna use compiler wrappers') + environment.update({ + 'CC': os.path.join(bin_dir, COMPILER_WRAPPER_CC), + 'CXX': os.path.join(bin_dir, COMPILER_WRAPPER_CXX), + 'INTERCEPT_BUILD_CC': c_compiler, + 'INTERCEPT_BUILD_CXX': cxx_compiler, + 'INTERCEPT_BUILD_VERBOSE': 'DEBUG' if args.verbose > 2 else 'INFO' + }) + elif sys.platform == 'darwin': + logging.debug('intercept gonna preload libear on OSX') + environment.update({ + 'DYLD_INSERT_LIBRARIES': libear_path, + 'DYLD_FORCE_FLAT_NAMESPACE': '1' + }) + else: + logging.debug('intercept gonna preload libear on UNIX') + environment.update({'LD_PRELOAD': libear_path}) + + return environment + + +def intercept_build_wrapper(cplusplus): + """ Entry point for `intercept-cc` and `intercept-c++` compiler wrappers. + + It does generate execution report into target directory. And execute + the wrapped compilation with the real compiler. The parameters for + report and execution are from environment variables. + + Those parameters which for 'libear' library can't have meaningful + values are faked. """ + + # initialize wrapper logging + logging.basicConfig(format='intercept: %(levelname)s: %(message)s', + level=os.getenv('INTERCEPT_BUILD_VERBOSE', 'INFO')) + # write report + try: + target_dir = os.getenv('INTERCEPT_BUILD_TARGET_DIR') + if not target_dir: + raise UserWarning('exec report target directory not found') + pid = str(os.getpid()) + target_file = os.path.join(target_dir, pid + '.cmd') + logging.debug('writing exec report to: %s', target_file) + with open(target_file, 'ab') as handler: + working_dir = os.getcwd() + command = US.join(sys.argv) + US + content = RS.join([pid, pid, 'wrapper', working_dir, command]) + GS + handler.write(content.encode('utf-8')) + except IOError: + logging.exception('writing exec report failed') + except UserWarning as warning: + logging.warning(warning) + # execute with real compiler + compiler = os.getenv('INTERCEPT_BUILD_CXX', 'c++') if cplusplus \ + else os.getenv('INTERCEPT_BUILD_CC', 'cc') + compilation = [compiler] + sys.argv[1:] + logging.debug('execute compiler: %s', compilation) + return subprocess.call(compilation) + + +def parse_exec_trace(filename): + """ Parse the file generated by the 'libear' preloaded library. + + Given filename points to a file which contains the basic report + generated by the interception library or wrapper command. A single + report file _might_ contain multiple process creation info. """ + + logging.debug('parse exec trace file: %s', filename) + with open(filename, 'r') as handler: + content = handler.read() + for group in filter(bool, content.split(GS)): + records = group.split(RS) + yield { + 'pid': records[0], + 'ppid': records[1], + 'function': records[2], + 'directory': records[3], + 'command': records[4].split(US)[:-1] + } + + +def format_entry(entry): + """ Generate the desired fields for compilation database entries. """ + + def abspath(cwd, name): + """ Create normalized absolute path from input filename. """ + fullname = name if os.path.isabs(name) else os.path.join(cwd, name) + return os.path.normpath(fullname) + + logging.debug('format this command: %s', entry['command']) + atoms = classify_parameters(entry['command']) + if atoms['action'] <= Action.Compile: + for source in atoms['files']: + compiler = 'c++' if atoms['c++'] else 'cc' + flags = atoms['compile_options'] + flags += ['-o', atoms['output']] if atoms['output'] else [] + flags += ['-x', atoms['language']] if 'language' in atoms else [] + flags += [elem + for arch in atoms.get('archs_seen', []) + for elem in ['-arch', arch]] + command = [compiler, '-c'] + flags + [source] + logging.debug('formated as: %s', command) + yield { + 'directory': entry['directory'], + 'command': encode(command), + 'file': abspath(entry['directory'], source) + } + + +def is_compiler_call(entry): + """ A predicate to decide the entry is a compiler call or not. """ + + patterns = [ + re.compile(r'^([^/]*/)*intercept-c(c|\+\+)$'), + re.compile(r'^([^/]*/)*c(c|\+\+)$'), + re.compile(r'^([^/]*/)*([^-]*-)*[mg](cc|\+\+)(-\d+(\.\d+){0,2})?$'), + re.compile(r'^([^/]*/)*([^-]*-)*clang(\+\+)?(-\d+(\.\d+){0,2})?$'), + re.compile(r'^([^/]*/)*llvm-g(cc|\+\+)$'), + ] + executable = entry['command'][0] + return any((pattern.match(executable) for pattern in patterns)) + + +def is_preload_disabled(platform): + """ Library-based interposition will fail silently if SIP is enabled, + so this should be detected. You can detect whether SIP is enabled on + Darwin by checking whether (1) there is a binary called 'csrutil' in + the path and, if so, (2) whether the output of executing 'csrutil status' + contains 'System Integrity Protection status: enabled'. + + Same problem on linux when SELinux is enabled. The status query program + 'sestatus' and the output when it's enabled 'SELinux status: enabled'. """ + + if platform == 'darwin': + pattern = re.compile(r'System Integrity Protection status:\s+enabled') + command = ['csrutil', 'status'] + elif platform in {'linux', 'linux2'}: + pattern = re.compile(r'SELinux status:\s+enabled') + command = ['sestatus'] + else: + return False + + try: + lines = subprocess.check_output(command).decode('utf-8') + return any((pattern.match(line) for line in lines.splitlines())) + except: + return False + + +def entry_hash(entry): + """ Implement unique hash method for compilation database entries. """ + + # For faster lookup in set filename is reverted + filename = entry['file'][::-1] + # For faster lookup in set directory is reverted + directory = entry['directory'][::-1] + # On OS X the 'cc' and 'c++' compilers are wrappers for + # 'clang' therefore both call would be logged. To avoid + # this the hash does not contain the first word of the + # command. + command = ' '.join(decode(entry['command'])[1:]) + + return '<>'.join([filename, directory, command]) + + +def create_parser(): + """ Command line argument parser factory method. """ + + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + '--verbose', '-v', + action='count', + default=0, + help="""Enable verbose output from '%(prog)s'. A second and third + flag increases verbosity.""") + parser.add_argument( + '--cdb', + metavar='<file>', + default="compile_commands.json", + help="""The JSON compilation database.""") + group = parser.add_mutually_exclusive_group() + group.add_argument( + '--append', + action='store_true', + help="""Append new entries to existing compilation database.""") + group.add_argument( + '--disable-filter', '-n', + dest='raw_entries', + action='store_true', + help="""Intercepted child process creation calls (exec calls) are all + logged to the output. The output is not a compilation database. + This flag is for debug purposes.""") + + advanced = parser.add_argument_group('advanced options') + advanced.add_argument( + '--override-compiler', + action='store_true', + help="""Always resort to the compiler wrapper even when better + intercept methods are available.""") + advanced.add_argument( + '--use-cc', + metavar='<path>', + dest='cc', + default='cc', + help="""When '%(prog)s' analyzes a project by interposing a compiler + wrapper, which executes a real compiler for compilation and + do other tasks (record the compiler invocation). Because of + this interposing, '%(prog)s' does not know what compiler your + project normally uses. Instead, it simply overrides the CC + environment variable, and guesses your default compiler. + + If you need '%(prog)s' to use a specific compiler for + *compilation* then you can use this option to specify a path + to that compiler.""") + advanced.add_argument( + '--use-c++', + metavar='<path>', + dest='cxx', + default='c++', + help="""This is the same as "--use-cc" but for C++ code.""") + + parser.add_argument( + dest='build', + nargs=argparse.REMAINDER, + help="""Command to run.""") + + return parser diff --git a/tools/scan-build-py/libscanbuild/report.py b/tools/scan-build-py/libscanbuild/report.py new file mode 100644 index 000000000000..efc0a55de619 --- /dev/null +++ b/tools/scan-build-py/libscanbuild/report.py @@ -0,0 +1,530 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +""" This module is responsible to generate 'index.html' for the report. + +The input for this step is the output directory, where individual reports +could be found. It parses those reports and generates 'index.html'. """ + +import re +import os +import os.path +import sys +import shutil +import time +import tempfile +import itertools +import plistlib +import glob +import json +import logging +import contextlib +from libscanbuild import duplicate_check +from libscanbuild.clang import get_version + +__all__ = ['report_directory', 'document'] + + +@contextlib.contextmanager +def report_directory(hint, keep): + """ Responsible for the report directory. + + hint -- could specify the parent directory of the output directory. + keep -- a boolean value to keep or delete the empty report directory. """ + + stamp = time.strftime('scan-build-%Y-%m-%d-%H%M%S-', time.localtime()) + name = tempfile.mkdtemp(prefix=stamp, dir=hint) + + logging.info('Report directory created: %s', name) + + try: + yield name + finally: + if os.listdir(name): + msg = "Run 'scan-view %s' to examine bug reports." + keep = True + else: + if keep: + msg = "Report directory '%s' contans no report, but kept." + else: + msg = "Removing directory '%s' because it contains no report." + logging.warning(msg, name) + + if not keep: + os.rmdir(name) + + +def document(args, output_dir, use_cdb): + """ Generates cover report and returns the number of bugs/crashes. """ + + html_reports_available = args.output_format in {'html', 'plist-html'} + + logging.debug('count crashes and bugs') + crash_count = sum(1 for _ in read_crashes(output_dir)) + bug_counter = create_counters() + for bug in read_bugs(output_dir, html_reports_available): + bug_counter(bug) + result = crash_count + bug_counter.total + + if html_reports_available and result: + logging.debug('generate index.html file') + # common prefix for source files to have sort filenames + prefix = commonprefix_from(args.cdb) if use_cdb else os.getcwd() + # assemble the cover from multiple fragments + try: + fragments = [] + if bug_counter.total: + fragments.append(bug_summary(output_dir, bug_counter)) + fragments.append(bug_report(output_dir, prefix)) + if crash_count: + fragments.append(crash_report(output_dir, prefix)) + assemble_cover(output_dir, prefix, args, fragments) + # copy additinal files to the report + copy_resource_files(output_dir) + if use_cdb: + shutil.copy(args.cdb, output_dir) + finally: + for fragment in fragments: + os.remove(fragment) + return result + + +def assemble_cover(output_dir, prefix, args, fragments): + """ Put together the fragments into a final report. """ + + import getpass + import socket + import datetime + + if args.html_title is None: + args.html_title = os.path.basename(prefix) + ' - analyzer results' + + with open(os.path.join(output_dir, 'index.html'), 'w') as handle: + indent = 0 + handle.write(reindent(""" + |<!DOCTYPE html> + |<html> + | <head> + | <title>{html_title}</title> + | <link type="text/css" rel="stylesheet" href="scanview.css"/> + | <script type='text/javascript' src="sorttable.js"></script> + | <script type='text/javascript' src='selectable.js'></script> + | </head>""", indent).format(html_title=args.html_title)) + handle.write(comment('SUMMARYENDHEAD')) + handle.write(reindent(""" + | <body> + | <h1>{html_title}</h1> + | <table> + | <tr><th>User:</th><td>{user_name}@{host_name}</td></tr> + | <tr><th>Working Directory:</th><td>{current_dir}</td></tr> + | <tr><th>Command Line:</th><td>{cmd_args}</td></tr> + | <tr><th>Clang Version:</th><td>{clang_version}</td></tr> + | <tr><th>Date:</th><td>{date}</td></tr> + | </table>""", indent).format(html_title=args.html_title, + user_name=getpass.getuser(), + host_name=socket.gethostname(), + current_dir=prefix, + cmd_args=' '.join(sys.argv), + clang_version=get_version(args.clang), + date=datetime.datetime.today( + ).strftime('%c'))) + for fragment in fragments: + # copy the content of fragments + with open(fragment, 'r') as input_handle: + shutil.copyfileobj(input_handle, handle) + handle.write(reindent(""" + | </body> + |</html>""", indent)) + + +def bug_summary(output_dir, bug_counter): + """ Bug summary is a HTML table to give a better overview of the bugs. """ + + name = os.path.join(output_dir, 'summary.html.fragment') + with open(name, 'w') as handle: + indent = 4 + handle.write(reindent(""" + |<h2>Bug Summary</h2> + |<table> + | <thead> + | <tr> + | <td>Bug Type</td> + | <td>Quantity</td> + | <td class="sorttable_nosort">Display?</td> + | </tr> + | </thead> + | <tbody>""", indent)) + handle.write(reindent(""" + | <tr style="font-weight:bold"> + | <td class="SUMM_DESC">All Bugs</td> + | <td class="Q">{0}</td> + | <td> + | <center> + | <input checked type="checkbox" id="AllBugsCheck" + | onClick="CopyCheckedStateToCheckButtons(this);"/> + | </center> + | </td> + | </tr>""", indent).format(bug_counter.total)) + for category, types in bug_counter.categories.items(): + handle.write(reindent(""" + | <tr> + | <th>{0}</th><th colspan=2></th> + | </tr>""", indent).format(category)) + for bug_type in types.values(): + handle.write(reindent(""" + | <tr> + | <td class="SUMM_DESC">{bug_type}</td> + | <td class="Q">{bug_count}</td> + | <td> + | <center> + | <input checked type="checkbox" + | onClick="ToggleDisplay(this,'{bug_type_class}');"/> + | </center> + | </td> + | </tr>""", indent).format(**bug_type)) + handle.write(reindent(""" + | </tbody> + |</table>""", indent)) + handle.write(comment('SUMMARYBUGEND')) + return name + + +def bug_report(output_dir, prefix): + """ Creates a fragment from the analyzer reports. """ + + pretty = prettify_bug(prefix, output_dir) + bugs = (pretty(bug) for bug in read_bugs(output_dir, True)) + + name = os.path.join(output_dir, 'bugs.html.fragment') + with open(name, 'w') as handle: + indent = 4 + handle.write(reindent(""" + |<h2>Reports</h2> + |<table class="sortable" style="table-layout:automatic"> + | <thead> + | <tr> + | <td>Bug Group</td> + | <td class="sorttable_sorted"> + | Bug Type + | <span id="sorttable_sortfwdind"> ▾</span> + | </td> + | <td>File</td> + | <td>Function/Method</td> + | <td class="Q">Line</td> + | <td class="Q">Path Length</td> + | <td class="sorttable_nosort"></td> + | </tr> + | </thead> + | <tbody>""", indent)) + handle.write(comment('REPORTBUGCOL')) + for current in bugs: + handle.write(reindent(""" + | <tr class="{bug_type_class}"> + | <td class="DESC">{bug_category}</td> + | <td class="DESC">{bug_type}</td> + | <td>{bug_file}</td> + | <td class="DESC">{bug_function}</td> + | <td class="Q">{bug_line}</td> + | <td class="Q">{bug_path_length}</td> + | <td><a href="{report_file}#EndPath">View Report</a></td> + | </tr>""", indent).format(**current)) + handle.write(comment('REPORTBUG', {'id': current['report_file']})) + handle.write(reindent(""" + | </tbody> + |</table>""", indent)) + handle.write(comment('REPORTBUGEND')) + return name + + +def crash_report(output_dir, prefix): + """ Creates a fragment from the compiler crashes. """ + + pretty = prettify_crash(prefix, output_dir) + crashes = (pretty(crash) for crash in read_crashes(output_dir)) + + name = os.path.join(output_dir, 'crashes.html.fragment') + with open(name, 'w') as handle: + indent = 4 + handle.write(reindent(""" + |<h2>Analyzer Failures</h2> + |<p>The analyzer had problems processing the following files:</p> + |<table> + | <thead> + | <tr> + | <td>Problem</td> + | <td>Source File</td> + | <td>Preprocessed File</td> + | <td>STDERR Output</td> + | </tr> + | </thead> + | <tbody>""", indent)) + for current in crashes: + handle.write(reindent(""" + | <tr> + | <td>{problem}</td> + | <td>{source}</td> + | <td><a href="{file}">preprocessor output</a></td> + | <td><a href="{stderr}">analyzer std err</a></td> + | </tr>""", indent).format(**current)) + handle.write(comment('REPORTPROBLEM', current)) + handle.write(reindent(""" + | </tbody> + |</table>""", indent)) + handle.write(comment('REPORTCRASHES')) + return name + + +def read_crashes(output_dir): + """ Generate a unique sequence of crashes from given output directory. """ + + return (parse_crash(filename) + for filename in glob.iglob(os.path.join(output_dir, 'failures', + '*.info.txt'))) + + +def read_bugs(output_dir, html): + """ Generate a unique sequence of bugs from given output directory. + + Duplicates can be in a project if the same module was compiled multiple + times with different compiler options. These would be better to show in + the final report (cover) only once. """ + + parser = parse_bug_html if html else parse_bug_plist + pattern = '*.html' if html else '*.plist' + + duplicate = duplicate_check( + lambda bug: '{bug_line}.{bug_path_length}:{bug_file}'.format(**bug)) + + bugs = itertools.chain.from_iterable( + # parser creates a bug generator not the bug itself + parser(filename) + for filename in glob.iglob(os.path.join(output_dir, pattern))) + + return (bug for bug in bugs if not duplicate(bug)) + + +def parse_bug_plist(filename): + """ Returns the generator of bugs from a single .plist file. """ + + content = plistlib.readPlist(filename) + files = content.get('files') + for bug in content.get('diagnostics', []): + if len(files) <= int(bug['location']['file']): + logging.warning('Parsing bug from "%s" failed', filename) + continue + + yield { + 'result': filename, + 'bug_type': bug['type'], + 'bug_category': bug['category'], + 'bug_line': int(bug['location']['line']), + 'bug_path_length': int(bug['location']['col']), + 'bug_file': files[int(bug['location']['file'])] + } + + +def parse_bug_html(filename): + """ Parse out the bug information from HTML output. """ + + patterns = [re.compile(r'<!-- BUGTYPE (?P<bug_type>.*) -->$'), + re.compile(r'<!-- BUGFILE (?P<bug_file>.*) -->$'), + re.compile(r'<!-- BUGPATHLENGTH (?P<bug_path_length>.*) -->$'), + re.compile(r'<!-- BUGLINE (?P<bug_line>.*) -->$'), + re.compile(r'<!-- BUGCATEGORY (?P<bug_category>.*) -->$'), + re.compile(r'<!-- BUGDESC (?P<bug_description>.*) -->$'), + re.compile(r'<!-- FUNCTIONNAME (?P<bug_function>.*) -->$')] + endsign = re.compile(r'<!-- BUGMETAEND -->') + + bug = { + 'report_file': filename, + 'bug_function': 'n/a', # compatibility with < clang-3.5 + 'bug_category': 'Other', + 'bug_line': 0, + 'bug_path_length': 1 + } + + with open(filename) as handler: + for line in handler.readlines(): + # do not read the file further + if endsign.match(line): + break + # search for the right lines + for regex in patterns: + match = regex.match(line.strip()) + if match: + bug.update(match.groupdict()) + break + + encode_value(bug, 'bug_line', int) + encode_value(bug, 'bug_path_length', int) + + yield bug + + +def parse_crash(filename): + """ Parse out the crash information from the report file. """ + + match = re.match(r'(.*)\.info\.txt', filename) + name = match.group(1) if match else None + with open(filename) as handler: + lines = handler.readlines() + return { + 'source': lines[0].rstrip(), + 'problem': lines[1].rstrip(), + 'file': name, + 'info': name + '.info.txt', + 'stderr': name + '.stderr.txt' + } + + +def category_type_name(bug): + """ Create a new bug attribute from bug by category and type. + + The result will be used as CSS class selector in the final report. """ + + def smash(key): + """ Make value ready to be HTML attribute value. """ + + return bug.get(key, '').lower().replace(' ', '_').replace("'", '') + + return escape('bt_' + smash('bug_category') + '_' + smash('bug_type')) + + +def create_counters(): + """ Create counters for bug statistics. + + Two entries are maintained: 'total' is an integer, represents the + number of bugs. The 'categories' is a two level categorisation of bug + counters. The first level is 'bug category' the second is 'bug type'. + Each entry in this classification is a dictionary of 'count', 'type' + and 'label'. """ + + def predicate(bug): + bug_category = bug['bug_category'] + bug_type = bug['bug_type'] + current_category = predicate.categories.get(bug_category, dict()) + current_type = current_category.get(bug_type, { + 'bug_type': bug_type, + 'bug_type_class': category_type_name(bug), + 'bug_count': 0 + }) + current_type.update({'bug_count': current_type['bug_count'] + 1}) + current_category.update({bug_type: current_type}) + predicate.categories.update({bug_category: current_category}) + predicate.total += 1 + + predicate.total = 0 + predicate.categories = dict() + return predicate + + +def prettify_bug(prefix, output_dir): + def predicate(bug): + """ Make safe this values to embed into HTML. """ + + bug['bug_type_class'] = category_type_name(bug) + + encode_value(bug, 'bug_file', lambda x: escape(chop(prefix, x))) + encode_value(bug, 'bug_category', escape) + encode_value(bug, 'bug_type', escape) + encode_value(bug, 'report_file', lambda x: escape(chop(output_dir, x))) + return bug + + return predicate + + +def prettify_crash(prefix, output_dir): + def predicate(crash): + """ Make safe this values to embed into HTML. """ + + encode_value(crash, 'source', lambda x: escape(chop(prefix, x))) + encode_value(crash, 'problem', escape) + encode_value(crash, 'file', lambda x: escape(chop(output_dir, x))) + encode_value(crash, 'info', lambda x: escape(chop(output_dir, x))) + encode_value(crash, 'stderr', lambda x: escape(chop(output_dir, x))) + return crash + + return predicate + + +def copy_resource_files(output_dir): + """ Copy the javascript and css files to the report directory. """ + + this_dir = os.path.dirname(os.path.realpath(__file__)) + for resource in os.listdir(os.path.join(this_dir, 'resources')): + shutil.copy(os.path.join(this_dir, 'resources', resource), output_dir) + + +def encode_value(container, key, encode): + """ Run 'encode' on 'container[key]' value and update it. """ + + if key in container: + value = encode(container[key]) + container.update({key: value}) + + +def chop(prefix, filename): + """ Create 'filename' from '/prefix/filename' """ + + return filename if not len(prefix) else os.path.relpath(filename, prefix) + + +def escape(text): + """ Paranoid HTML escape method. (Python version independent) """ + + escape_table = { + '&': '&', + '"': '"', + "'": ''', + '>': '>', + '<': '<' + } + return ''.join(escape_table.get(c, c) for c in text) + + +def reindent(text, indent): + """ Utility function to format html output and keep indentation. """ + + result = '' + for line in text.splitlines(): + if len(line.strip()): + result += ' ' * indent + line.split('|')[1] + os.linesep + return result + + +def comment(name, opts=dict()): + """ Utility function to format meta information as comment. """ + + attributes = '' + for key, value in opts.items(): + attributes += ' {0}="{1}"'.format(key, value) + + return '<!-- {0}{1} -->{2}'.format(name, attributes, os.linesep) + + +def commonprefix_from(filename): + """ Create file prefix from a compilation database entries. """ + + with open(filename, 'r') as handle: + return commonprefix(item['file'] for item in json.load(handle)) + + +def commonprefix(files): + """ Fixed version of os.path.commonprefix. Return the longest path prefix + that is a prefix of all paths in filenames. """ + + result = None + for current in files: + if result is not None: + result = os.path.commonprefix([result, current]) + else: + result = current + + if result is None: + return '' + elif not os.path.isdir(result): + return os.path.dirname(result) + else: + return os.path.abspath(result) diff --git a/tools/scan-build-py/libscanbuild/resources/scanview.css b/tools/scan-build-py/libscanbuild/resources/scanview.css new file mode 100644 index 000000000000..cf8a5a6ad470 --- /dev/null +++ b/tools/scan-build-py/libscanbuild/resources/scanview.css @@ -0,0 +1,62 @@ +body { color:#000000; background-color:#ffffff } +body { font-family: Helvetica, sans-serif; font-size:9pt } +h1 { font-size: 14pt; } +h2 { font-size: 12pt; } +table { font-size:9pt } +table { border-spacing: 0px; border: 1px solid black } +th, table thead { + background-color:#eee; color:#666666; + font-weight: bold; cursor: default; + text-align:center; + font-weight: bold; font-family: Verdana; + white-space:nowrap; +} +.W { font-size:0px } +th, td { padding:5px; padding-left:8px; text-align:left } +td.SUMM_DESC { padding-left:12px } +td.DESC { white-space:pre } +td.Q { text-align:right } +td { text-align:left } +tbody.scrollContent { overflow:auto } + +table.form_group { + background-color: #ccc; + border: 1px solid #333; + padding: 2px; +} + +table.form_inner_group { + background-color: #ccc; + border: 1px solid #333; + padding: 0px; +} + +table.form { + background-color: #999; + border: 1px solid #333; + padding: 2px; +} + +td.form_label { + text-align: right; + vertical-align: top; +} +/* For one line entires */ +td.form_clabel { + text-align: right; + vertical-align: center; +} +td.form_value { + text-align: left; + vertical-align: top; +} +td.form_submit { + text-align: right; + vertical-align: top; +} + +h1.SubmitFail { + color: #f00; +} +h1.SubmitOk { +} diff --git a/tools/scan-build-py/libscanbuild/resources/selectable.js b/tools/scan-build-py/libscanbuild/resources/selectable.js new file mode 100644 index 000000000000..53f6a8da13d8 --- /dev/null +++ b/tools/scan-build-py/libscanbuild/resources/selectable.js @@ -0,0 +1,47 @@ +function SetDisplay(RowClass, DisplayVal) +{ + var Rows = document.getElementsByTagName("tr"); + for ( var i = 0 ; i < Rows.length; ++i ) { + if (Rows[i].className == RowClass) { + Rows[i].style.display = DisplayVal; + } + } +} + +function CopyCheckedStateToCheckButtons(SummaryCheckButton) { + var Inputs = document.getElementsByTagName("input"); + for ( var i = 0 ; i < Inputs.length; ++i ) { + if (Inputs[i].type == "checkbox") { + if(Inputs[i] != SummaryCheckButton) { + Inputs[i].checked = SummaryCheckButton.checked; + Inputs[i].onclick(); + } + } + } +} + +function returnObjById( id ) { + if (document.getElementById) + var returnVar = document.getElementById(id); + else if (document.all) + var returnVar = document.all[id]; + else if (document.layers) + var returnVar = document.layers[id]; + return returnVar; +} + +var NumUnchecked = 0; + +function ToggleDisplay(CheckButton, ClassName) { + if (CheckButton.checked) { + SetDisplay(ClassName, ""); + if (--NumUnchecked == 0) { + returnObjById("AllBugsCheck").checked = true; + } + } + else { + SetDisplay(ClassName, "none"); + NumUnchecked++; + returnObjById("AllBugsCheck").checked = false; + } +} diff --git a/tools/scan-build-py/libscanbuild/resources/sorttable.js b/tools/scan-build-py/libscanbuild/resources/sorttable.js new file mode 100644 index 000000000000..32faa078d899 --- /dev/null +++ b/tools/scan-build-py/libscanbuild/resources/sorttable.js @@ -0,0 +1,492 @@ +/* + SortTable + version 2 + 7th April 2007 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + + Instructions: + Download this file + Add <script src="sorttable.js"></script> to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + + +var stIsIE = /*@cc_on!@*/false; + +sorttable = { + init: function() { + // quit if this function has already been called + if (arguments.callee.done) return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + + }, + + makeSortable: function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the,table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backward compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i=0; i<table.rows.length; i++) { + if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { + sortbottomrows[sortbottomrows.length] = table.rows[i]; + } + } + if (sortbottomrows) { + if (table.tFoot == null) { + // table doesn't have a tfoot. Create one. + tfo = document.createElement('tfoot'); + table.appendChild(tfo); + } + for (var i=0; i<sortbottomrows.length; i++) { + tfo.appendChild(sortbottomrows[i]); + } + delete sortbottomrows; + } + + // work through each column and calculate its type + headrow = table.tHead.rows[0].cells; + for (var i=0; i<headrow.length; i++) { + // manually override the type with a sorttable_type attribute + if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col + mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); + if (mtch) { override = mtch[1]; } + if (mtch && typeof sorttable["sort_"+override] == 'function') { + headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; + } else { + headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); + } + // make it clickable to sort + headrow[i].sorttable_columnindex = i; + headrow[i].sorttable_tbody = table.tBodies[0]; + dean_addEvent(headrow[i],"click", function(e) { + + if (this.className.search(/\bsorttable_sorted\b/) != -1) { + // if we're already sorted by this column, just + // reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted', + 'sorttable_sorted_reverse'); + this.removeChild(document.getElementById('sorttable_sortfwdind')); + sortrevind = document.createElement('span'); + sortrevind.id = "sorttable_sortrevind"; + sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = cell.className.replace('sorttable_sorted_reverse',''); + cell.className = cell.className.replace('sorttable_sorted',''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j=0; j<rows.length; j++) { + row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]]; + } + /* If you want a stable sort, uncomment the following line */ + sorttable.shaker_sort(row_array, this.sorttable_sortfunction); + /* and comment out this one */ + //row_array.sort(this.sorttable_sortfunction); + + tb = this.sorttable_tbody; + for (var j=0; j<row_array.length; j++) { + tb.appendChild(row_array[j][1]); + } + + delete row_array; + }); + } + } + }, + + guessType: function(table, column) { + // guess the type of a column based on its first non-blank row + sortfn = sorttable.sort_alpha; + for (var i=0; i<table.tBodies[0].rows.length; i++) { + text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); + if (text != '') { + if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) { + return sorttable.sort_numeric; + } + // check for a date: dd/mm/yyyy or dd/mm/yy + // can have / or . or - as separator + // can be mm/dd as well + possdate = text.match(sorttable.DATE_RE) + if (possdate) { + // looks like a date + first = parseInt(possdate[1]); + second = parseInt(possdate[2]); + if (first > 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText: function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for <input> fields. + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } + else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } + else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse: function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i=0; i<tbody.rows.length; i++) { + newrows[newrows.length] = tbody.rows[i]; + } + for (var i=newrows.length-1; i>=0; i--) { + tbody.appendChild(newrows[i]); + } + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric: function(a,b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); + if (isNaN(bb)) bb = 0; + return aa-bb; + }, + sort_alpha: function(a,b) { + if (a[0]==b[0]) return 0; + if (a[0]<b[0]) return -1; + return 1; + }, + sort_ddmm: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + sort_mmdd: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + + shaker_sort: function(list, comp_func) { + // A stable sort function to allow multi-level sorting of data + // see: http://en.wikipedia.org/wiki/Cocktail_sort + // thanks to Joseph Nahmias + var b = 0; + var t = list.length - 1; + var swap = true; + + while(swap) { + swap = false; + for(var i = b; i < t; ++i) { + if ( comp_func(list[i], list[i+1]) > 0 ) { + var q = list[i]; list[i] = list[i+1]; list[i+1] = q; + swap = true; + } + } // for + t--; + + if (!swap) break; + + for(var i = t; i > b; --i) { + if ( comp_func(list[i], list[i-1]) < 0 ) { + var q = list[i]; list[i] = list[i-1]; list[i-1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for Mozilla/Opera9 */ +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", sorttable.init, false); +} + +/* for Internet Explorer */ +/*@cc_on @*/ +/*@if (@_win32) + document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>"); + var script = document.getElementById("__ie_onload"); + script.onreadystatechange = function() { + if (this.readyState == "complete") { + sorttable.init(); // call the onload handler + } + }; +/*@end @*/ + +/* for Safari */ +if (/WebKit/i.test(navigator.userAgent)) { // sniff + var _timer = setInterval(function() { + if (/loaded|complete/.test(document.readyState)) { + sorttable.init(); // call the onload handler + } + }, 10); +} + +/* for other browsers */ +window.onload = sorttable.init; + +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini + +// http://dean.edwards.name/weblog/2005/10/add-event/ + +function dean_addEvent(element, type, handler) { + if (element.addEventListener) { + element.addEventListener(type, handler, false); + } else { + // assign each event handler a unique ID + if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++; + // create a hash table of event types for the element + if (!element.events) element.events = {}; + // create a hash table of event handlers for each element/event pair + var handlers = element.events[type]; + if (!handlers) { + handlers = element.events[type] = {}; + // store the existing event handler (if there is one) + if (element["on" + type]) { + handlers[0] = element["on" + type]; + } + } + // store the event handler in the hash table + handlers[handler.$$guid] = handler; + // assign a global event handler to do all the work + element["on" + type] = handleEvent; + } +}; +// a counter used to create unique IDs +dean_addEvent.guid = 1; + +function removeEvent(element, type, handler) { + if (element.removeEventListener) { + element.removeEventListener(type, handler, false); + } else { + // delete the event handler from the hash table + if (element.events && element.events[type]) { + delete element.events[type][handler.$$guid]; + } + } +}; + +function handleEvent(event) { + var returnValue = true; + // grab the event object (IE uses a global event object) + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); + // get a reference to the hash table of event handlers + var handlers = this.events[event.type]; + // execute each event handler + for (var i in handlers) { + this.$$handleEvent = handlers[i]; + if (this.$$handleEvent(event) === false) { + returnValue = false; + } + } + return returnValue; +}; + +function fixEvent(event) { + // add W3C standard event methods + event.preventDefault = fixEvent.preventDefault; + event.stopPropagation = fixEvent.stopPropagation; + return event; +}; +fixEvent.preventDefault = function() { + this.returnValue = false; +}; +fixEvent.stopPropagation = function() { + this.cancelBubble = true; +} + +// Dean's forEach: http://dean.edwards.name/base/forEach.js +/* + forEach, version 1.0 + Copyright 2006, Dean Edwards + License: http://www.opensource.org/licenses/mit-license.php +*/ + +// array-like enumeration +if (!Array.forEach) { // mozilla already supports this + Array.forEach = function(array, block, context) { + for (var i = 0; i < array.length; i++) { + block.call(context, array[i], i, array); + } + }; +} + +// generic enumeration +Function.prototype.forEach = function(object, block, context) { + for (var key in object) { + if (typeof this.prototype[key] == "undefined") { + block.call(context, object[key], key, object); + } + } +}; + +// character enumeration +String.forEach = function(string, block, context) { + Array.forEach(string.split(""), function(chr, index) { + block.call(context, chr, index, string); + }); +}; + +// globally resolve forEach enumeration +var forEach = function(object, block, context) { + if (object) { + var resolve = Object; // default + if (object instanceof Function) { + // functions have a "length" property + resolve = Function; + } else if (object.forEach instanceof Function) { + // the object implements a custom forEach method so use that + object.forEach(block, context); + return; + } else if (typeof object == "string") { + // the object is a string + resolve = String; + } else if (typeof object.length == "number") { + // the object is array-like + resolve = Array; + } + resolve.forEach(object, block, context); + } +}; diff --git a/tools/scan-build-py/libscanbuild/runner.py b/tools/scan-build-py/libscanbuild/runner.py new file mode 100644 index 000000000000..248ca90ad3e6 --- /dev/null +++ b/tools/scan-build-py/libscanbuild/runner.py @@ -0,0 +1,256 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +""" This module is responsible to run the analyzer commands. """ + +import os +import os.path +import tempfile +import functools +import subprocess +import logging +from libscanbuild.command import classify_parameters, Action, classify_source +from libscanbuild.clang import get_arguments, get_version +from libscanbuild.shell import decode + +__all__ = ['run'] + + +def require(required): + """ Decorator for checking the required values in state. + + It checks the required attributes in the passed state and stop when + any of those is missing. """ + + def decorator(function): + @functools.wraps(function) + def wrapper(*args, **kwargs): + for key in required: + if key not in args[0]: + raise KeyError( + '{0} not passed to {1}'.format(key, function.__name__)) + + return function(*args, **kwargs) + + return wrapper + + return decorator + + +@require(['command', 'directory', 'file', # an entry from compilation database + 'clang', 'direct_args', # compiler name, and arguments from command + 'output_dir', 'output_format', 'output_failures']) +def run(opts): + """ Entry point to run (or not) static analyzer against a single entry + of the compilation database. + + This complex task is decomposed into smaller methods which are calling + each other in chain. If the analyzis is not possibe the given method + just return and break the chain. + + The passed parameter is a python dictionary. Each method first check + that the needed parameters received. (This is done by the 'require' + decorator. It's like an 'assert' to check the contract between the + caller and the called method.) """ + + try: + command = opts.pop('command') + logging.debug("Run analyzer against '%s'", command) + opts.update(classify_parameters(decode(command))) + + return action_check(opts) + except Exception: + logging.error("Problem occured during analyzis.", exc_info=1) + return None + + +@require(['report', 'directory', 'clang', 'output_dir', 'language', 'file', + 'error_type', 'error_output', 'exit_code']) +def report_failure(opts): + """ Create report when analyzer failed. + + The major report is the preprocessor output. The output filename generated + randomly. The compiler output also captured into '.stderr.txt' file. + And some more execution context also saved into '.info.txt' file. """ + + def extension(opts): + """ Generate preprocessor file extension. """ + + mapping = {'objective-c++': '.mii', 'objective-c': '.mi', 'c++': '.ii'} + return mapping.get(opts['language'], '.i') + + def destination(opts): + """ Creates failures directory if not exits yet. """ + + name = os.path.join(opts['output_dir'], 'failures') + if not os.path.isdir(name): + os.makedirs(name) + return name + + error = opts['error_type'] + (handle, name) = tempfile.mkstemp(suffix=extension(opts), + prefix='clang_' + error + '_', + dir=destination(opts)) + os.close(handle) + cwd = opts['directory'] + cmd = get_arguments([opts['clang']] + opts['report'] + ['-o', name], cwd) + logging.debug('exec command in %s: %s', cwd, ' '.join(cmd)) + subprocess.call(cmd, cwd=cwd) + + with open(name + '.info.txt', 'w') as handle: + handle.write(opts['file'] + os.linesep) + handle.write(error.title().replace('_', ' ') + os.linesep) + handle.write(' '.join(cmd) + os.linesep) + handle.write(' '.join(os.uname()) + os.linesep) + handle.write(get_version(cmd[0])) + handle.close() + + with open(name + '.stderr.txt', 'w') as handle: + handle.writelines(opts['error_output']) + handle.close() + + return { + 'error_output': opts['error_output'], + 'exit_code': opts['exit_code'] + } + + +@require(['clang', 'analyze', 'directory', 'output']) +def run_analyzer(opts, continuation=report_failure): + """ It assembles the analysis command line and executes it. Capture the + output of the analysis and returns with it. If failure reports are + requested, it calls the continuation to generate it. """ + + cwd = opts['directory'] + cmd = get_arguments([opts['clang']] + opts['analyze'] + opts['output'], + cwd) + logging.debug('exec command in %s: %s', cwd, ' '.join(cmd)) + child = subprocess.Popen(cmd, + cwd=cwd, + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + output = child.stdout.readlines() + child.stdout.close() + # do report details if it were asked + child.wait() + if opts.get('output_failures', False) and child.returncode: + error_type = 'crash' if child.returncode & 127 else 'other_error' + opts.update({ + 'error_type': error_type, + 'error_output': output, + 'exit_code': child.returncode + }) + return continuation(opts) + return {'error_output': output, 'exit_code': child.returncode} + + +@require(['output_dir']) +def set_analyzer_output(opts, continuation=run_analyzer): + """ Create output file if was requested. + + This plays a role only if .plist files are requested. """ + + if opts.get('output_format') in {'plist', 'plist-html'}: + with tempfile.NamedTemporaryFile(prefix='report-', + suffix='.plist', + delete=False, + dir=opts['output_dir']) as output: + opts.update({'output': ['-o', output.name]}) + return continuation(opts) + else: + opts.update({'output': ['-o', opts['output_dir']]}) + return continuation(opts) + + +@require(['file', 'directory', 'clang', 'direct_args', 'language', + 'output_dir', 'output_format', 'output_failures']) +def create_commands(opts, continuation=set_analyzer_output): + """ Create command to run analyzer or failure report generation. + + It generates commands (from compilation database entries) which contains + enough information to run the analyzer (and the crash report generation + if that was requested). """ + + common = [] + if 'arch' in opts: + common.extend(['-arch', opts.pop('arch')]) + common.extend(opts.pop('compile_options', [])) + common.extend(['-x', opts['language']]) + common.append(os.path.relpath(opts['file'], opts['directory'])) + + opts.update({ + 'analyze': ['--analyze'] + opts['direct_args'] + common, + 'report': ['-fsyntax-only', '-E'] + common + }) + + return continuation(opts) + + +@require(['file', 'c++']) +def language_check(opts, continuation=create_commands): + """ Find out the language from command line parameters or file name + extension. The decision also influenced by the compiler invocation. """ + + accepteds = { + 'c', 'c++', 'objective-c', 'objective-c++', 'c-cpp-output', + 'c++-cpp-output', 'objective-c-cpp-output' + } + + key = 'language' + language = opts[key] if key in opts else \ + classify_source(opts['file'], opts['c++']) + + if language is None: + logging.debug('skip analysis, language not known') + return None + elif language not in accepteds: + logging.debug('skip analysis, language not supported') + return None + else: + logging.debug('analysis, language: %s', language) + opts.update({key: language}) + return continuation(opts) + + +@require([]) +def arch_check(opts, continuation=language_check): + """ Do run analyzer through one of the given architectures. """ + + disableds = {'ppc', 'ppc64'} + + key = 'archs_seen' + if key in opts: + # filter out disabled architectures and -arch switches + archs = [a for a in opts[key] if a not in disableds] + + if not archs: + logging.debug('skip analysis, found not supported arch') + return None + else: + # There should be only one arch given (or the same multiple + # times). If there are multiple arch are given and are not + # the same, those should not change the pre-processing step. + # But that's the only pass we have before run the analyzer. + arch = archs.pop() + logging.debug('analysis, on arch: %s', arch) + + opts.update({'arch': arch}) + del opts[key] + return continuation(opts) + else: + logging.debug('analysis, on default arch') + return continuation(opts) + + +@require(['action']) +def action_check(opts, continuation=arch_check): + """ Continue analysis only if it compilation or link. """ + + if opts.pop('action') <= Action.Compile: + return continuation(opts) + else: + logging.debug('skip analysis, not compilation nor link') + return None diff --git a/tools/scan-build-py/libscanbuild/shell.py b/tools/scan-build-py/libscanbuild/shell.py new file mode 100644 index 000000000000..a575946a9540 --- /dev/null +++ b/tools/scan-build-py/libscanbuild/shell.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +""" This module implements basic shell escaping/unescaping methods. """ + +import re +import shlex + +__all__ = ['encode', 'decode'] + + +def encode(command): + """ Takes a command as list and returns a string. """ + + def needs_quote(word): + """ Returns true if arguments needs to be protected by quotes. + + Previous implementation was shlex.split method, but that's not good + for this job. Currently is running through the string with a basic + state checking. """ + + reserved = {' ', '$', '%', '&', '(', ')', '[', ']', '{', '}', '*', '|', + '<', '>', '@', '?', '!'} + state = 0 + for current in word: + if state == 0 and current in reserved: + return True + elif state == 0 and current == '\\': + state = 1 + elif state == 1 and current in reserved | {'\\'}: + state = 0 + elif state == 0 and current == '"': + state = 2 + elif state == 2 and current == '"': + state = 0 + elif state == 0 and current == "'": + state = 3 + elif state == 3 and current == "'": + state = 0 + return state != 0 + + def escape(word): + """ Do protect argument if that's needed. """ + + table = {'\\': '\\\\', '"': '\\"'} + escaped = ''.join([table.get(c, c) for c in word]) + + return '"' + escaped + '"' if needs_quote(word) else escaped + + return " ".join([escape(arg) for arg in command]) + + +def decode(string): + """ Takes a command string and returns as a list. """ + + def unescape(arg): + """ Gets rid of the escaping characters. """ + + if len(arg) >= 2 and arg[0] == arg[-1] and arg[0] == '"': + arg = arg[1:-1] + return re.sub(r'\\(["\\])', r'\1', arg) + return re.sub(r'\\([\\ $%&\(\)\[\]\{\}\*|<>@?!])', r'\1', arg) + + return [unescape(arg) for arg in shlex.split(string)] diff --git a/tools/scan-build-py/tests/__init__.py b/tools/scan-build-py/tests/__init__.py new file mode 100644 index 000000000000..bde2376a6721 --- /dev/null +++ b/tools/scan-build-py/tests/__init__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import unittest + +import tests.unit +import tests.functional.cases + + +def suite(): + loader = unittest.TestLoader() + suite = unittest.TestSuite() + suite.addTests(loader.loadTestsFromModule(tests.unit)) + suite.addTests(loader.loadTestsFromModule(tests.functional.cases)) + return suite diff --git a/tools/scan-build-py/tests/functional/__init__.py b/tools/scan-build-py/tests/functional/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/scan-build-py/tests/functional/__init__.py diff --git a/tools/scan-build-py/tests/functional/cases/__init__.py b/tools/scan-build-py/tests/functional/cases/__init__.py new file mode 100644 index 000000000000..8fb84657029a --- /dev/null +++ b/tools/scan-build-py/tests/functional/cases/__init__.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import re +import os.path +import subprocess + + +def load_tests(loader, suite, pattern): + from . import test_from_cdb + suite.addTests(loader.loadTestsFromModule(test_from_cdb)) + from . import test_from_cmd + suite.addTests(loader.loadTestsFromModule(test_from_cmd)) + from . import test_create_cdb + suite.addTests(loader.loadTestsFromModule(test_create_cdb)) + from . import test_exec_anatomy + suite.addTests(loader.loadTestsFromModule(test_exec_anatomy)) + return suite + + +def make_args(target): + this_dir, _ = os.path.split(__file__) + path = os.path.normpath(os.path.join(this_dir, '..', 'src')) + return ['make', 'SRCDIR={}'.format(path), 'OBJDIR={}'.format(target), '-f', + os.path.join(path, 'build', 'Makefile')] + + +def silent_call(cmd, *args, **kwargs): + kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT}) + return subprocess.call(cmd, *args, **kwargs) + + +def silent_check_call(cmd, *args, **kwargs): + kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT}) + return subprocess.check_call(cmd, *args, **kwargs) + + +def call_and_report(analyzer_cmd, build_cmd): + child = subprocess.Popen(analyzer_cmd + ['-v'] + build_cmd, + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + + pattern = re.compile('Report directory created: (.+)') + directory = None + for line in child.stdout.readlines(): + match = pattern.search(line) + if match and match.lastindex == 1: + directory = match.group(1) + break + child.stdout.close() + child.wait() + + return (child.returncode, directory) + + +def check_call_and_report(analyzer_cmd, build_cmd): + exit_code, result = call_and_report(analyzer_cmd, build_cmd) + if exit_code != 0: + raise subprocess.CalledProcessError( + exit_code, analyzer_cmd + build_cmd, None) + else: + return result + + +def create_empty_file(filename): + with open(filename, 'a') as handle: + pass diff --git a/tools/scan-build-py/tests/functional/cases/test_create_cdb.py b/tools/scan-build-py/tests/functional/cases/test_create_cdb.py new file mode 100644 index 000000000000..6d449ba39c0b --- /dev/null +++ b/tools/scan-build-py/tests/functional/cases/test_create_cdb.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +from ...unit import fixtures +from . import make_args, silent_check_call, silent_call, create_empty_file +import unittest + +import os.path +import json + + +class CompilationDatabaseTest(unittest.TestCase): + @staticmethod + def run_intercept(tmpdir, args): + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + args + silent_check_call( + ['intercept-build', '--cdb', result] + make) + return result + + @staticmethod + def count_entries(filename): + with open(filename, 'r') as handler: + content = json.load(handler) + return len(content) + + def test_successful_build(self): + with fixtures.TempDir() as tmpdir: + result = self.run_intercept(tmpdir, ['build_regular']) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(5, self.count_entries(result)) + + def test_successful_build_with_wrapper(self): + with fixtures.TempDir() as tmpdir: + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + ['build_regular'] + silent_check_call(['intercept-build', '--cdb', result, + '--override-compiler'] + make) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(5, self.count_entries(result)) + + @unittest.skipIf(os.getenv('TRAVIS'), 'ubuntu make return -11') + def test_successful_build_parallel(self): + with fixtures.TempDir() as tmpdir: + result = self.run_intercept(tmpdir, ['-j', '4', 'build_regular']) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(5, self.count_entries(result)) + + @unittest.skipIf(os.getenv('TRAVIS'), 'ubuntu env remove clang from path') + def test_successful_build_on_empty_env(self): + with fixtures.TempDir() as tmpdir: + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + ['CC=clang', 'build_regular'] + silent_check_call(['intercept-build', '--cdb', result, + 'env', '-'] + make) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(5, self.count_entries(result)) + + def test_successful_build_all_in_one(self): + with fixtures.TempDir() as tmpdir: + result = self.run_intercept(tmpdir, ['build_all_in_one']) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(5, self.count_entries(result)) + + def test_not_successful_build(self): + with fixtures.TempDir() as tmpdir: + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + ['build_broken'] + silent_call( + ['intercept-build', '--cdb', result] + make) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(2, self.count_entries(result)) + + +class ExitCodeTest(unittest.TestCase): + @staticmethod + def run_intercept(tmpdir, target): + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + [target] + return silent_call( + ['intercept-build', '--cdb', result] + make) + + def test_successful_build(self): + with fixtures.TempDir() as tmpdir: + exitcode = self.run_intercept(tmpdir, 'build_clean') + self.assertFalse(exitcode) + + def test_not_successful_build(self): + with fixtures.TempDir() as tmpdir: + exitcode = self.run_intercept(tmpdir, 'build_broken') + self.assertTrue(exitcode) + + +class ResumeFeatureTest(unittest.TestCase): + @staticmethod + def run_intercept(tmpdir, target, args): + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + [target] + silent_check_call( + ['intercept-build', '--cdb', result] + args + make) + return result + + @staticmethod + def count_entries(filename): + with open(filename, 'r') as handler: + content = json.load(handler) + return len(content) + + def test_overwrite_existing_cdb(self): + with fixtures.TempDir() as tmpdir: + result = self.run_intercept(tmpdir, 'build_clean', []) + self.assertTrue(os.path.isfile(result)) + result = self.run_intercept(tmpdir, 'build_regular', []) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(2, self.count_entries(result)) + + def test_append_to_existing_cdb(self): + with fixtures.TempDir() as tmpdir: + result = self.run_intercept(tmpdir, 'build_clean', []) + self.assertTrue(os.path.isfile(result)) + result = self.run_intercept(tmpdir, 'build_regular', ['--append']) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(5, self.count_entries(result)) + + +class ResultFormatingTest(unittest.TestCase): + @staticmethod + def run_intercept(tmpdir, command): + result = os.path.join(tmpdir, 'cdb.json') + silent_check_call( + ['intercept-build', '--cdb', result] + command, + cwd=tmpdir) + with open(result, 'r') as handler: + content = json.load(handler) + return content + + def assert_creates_number_of_entries(self, command, count): + with fixtures.TempDir() as tmpdir: + filename = os.path.join(tmpdir, 'test.c') + create_empty_file(filename) + command.append(filename) + cmd = ['sh', '-c', ' '.join(command)] + cdb = self.run_intercept(tmpdir, cmd) + self.assertEqual(count, len(cdb)) + + def test_filter_preprocessor_only_calls(self): + self.assert_creates_number_of_entries(['cc', '-c'], 1) + self.assert_creates_number_of_entries(['cc', '-c', '-E'], 0) + self.assert_creates_number_of_entries(['cc', '-c', '-M'], 0) + self.assert_creates_number_of_entries(['cc', '-c', '-MM'], 0) + + def assert_command_creates_entry(self, command, expected): + with fixtures.TempDir() as tmpdir: + filename = os.path.join(tmpdir, command[-1]) + create_empty_file(filename) + cmd = ['sh', '-c', ' '.join(command)] + cdb = self.run_intercept(tmpdir, cmd) + self.assertEqual(' '.join(expected), cdb[0]['command']) + + def test_filter_preprocessor_flags(self): + self.assert_command_creates_entry( + ['cc', '-c', '-MD', 'test.c'], + ['cc', '-c', 'test.c']) + self.assert_command_creates_entry( + ['cc', '-c', '-MMD', 'test.c'], + ['cc', '-c', 'test.c']) + self.assert_command_creates_entry( + ['cc', '-c', '-MD', '-MF', 'test.d', 'test.c'], + ['cc', '-c', 'test.c']) + + def test_pass_language_flag(self): + self.assert_command_creates_entry( + ['cc', '-c', '-x', 'c', 'test.c'], + ['cc', '-c', '-x', 'c', 'test.c']) + self.assert_command_creates_entry( + ['cc', '-c', 'test.c'], + ['cc', '-c', 'test.c']) + + def test_pass_arch_flags(self): + self.assert_command_creates_entry( + ['clang', '-c', 'test.c'], + ['cc', '-c', 'test.c']) + self.assert_command_creates_entry( + ['clang', '-c', '-arch', 'i386', 'test.c'], + ['cc', '-c', '-arch', 'i386', 'test.c']) + self.assert_command_creates_entry( + ['clang', '-c', '-arch', 'i386', '-arch', 'armv7l', 'test.c'], + ['cc', '-c', '-arch', 'i386', '-arch', 'armv7l', 'test.c']) diff --git a/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py b/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py new file mode 100644 index 000000000000..329a477e03d7 --- /dev/null +++ b/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +from ...unit import fixtures +import unittest + +import os.path +import subprocess +import json + + +def run(source_dir, target_dir): + def execute(cmd): + return subprocess.check_call(cmd, + cwd=target_dir, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + + execute(['cmake', source_dir]) + execute(['make']) + + result_file = os.path.join(target_dir, 'result.json') + expected_file = os.path.join(target_dir, 'expected.json') + execute(['intercept-build', '--cdb', result_file, './exec', + expected_file]) + return (expected_file, result_file) + + +class ExecAnatomyTest(unittest.TestCase): + def assertEqualJson(self, expected, result): + def read_json(filename): + with open(filename) as handler: + return json.load(handler) + + lhs = read_json(expected) + rhs = read_json(result) + for item in lhs: + self.assertTrue(rhs.count(item)) + for item in rhs: + self.assertTrue(lhs.count(item)) + + def test_all_exec_calls(self): + this_dir, _ = os.path.split(__file__) + source_dir = os.path.normpath(os.path.join(this_dir, '..', 'exec')) + with fixtures.TempDir() as tmp_dir: + expected, result = run(source_dir, tmp_dir) + self.assertEqualJson(expected, result) diff --git a/tools/scan-build-py/tests/functional/cases/test_from_cdb.py b/tools/scan-build-py/tests/functional/cases/test_from_cdb.py new file mode 100644 index 000000000000..c579020db22c --- /dev/null +++ b/tools/scan-build-py/tests/functional/cases/test_from_cdb.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +from ...unit import fixtures +from . import call_and_report +import unittest + +import os.path +import string +import subprocess +import glob + + +def prepare_cdb(name, target_dir): + target_file = 'build_{0}.json'.format(name) + this_dir, _ = os.path.split(__file__) + path = os.path.normpath(os.path.join(this_dir, '..', 'src')) + source_dir = os.path.join(path, 'compilation_database') + source_file = os.path.join(source_dir, target_file + '.in') + target_file = os.path.join(target_dir, 'compile_commands.json') + with open(source_file, 'r') as in_handle: + with open(target_file, 'w') as out_handle: + for line in in_handle: + temp = string.Template(line) + out_handle.write(temp.substitute(path=path)) + return target_file + + +def run_analyzer(directory, cdb, args): + cmd = ['analyze-build', '--cdb', cdb, '--output', directory] \ + + args + return call_and_report(cmd, []) + + +class OutputDirectoryTest(unittest.TestCase): + def test_regular_keeps_report_dir(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, []) + self.assertTrue(os.path.isdir(reportdir)) + + def test_clear_deletes_report_dir(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('clean', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, []) + self.assertFalse(os.path.isdir(reportdir)) + + def test_clear_keeps_report_dir_when_asked(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('clean', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--keep-empty']) + self.assertTrue(os.path.isdir(reportdir)) + + +class ExitCodeTest(unittest.TestCase): + def test_regular_does_not_set_exit_code(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, __ = run_analyzer(tmpdir, cdb, []) + self.assertFalse(exit_code) + + def test_clear_does_not_set_exit_code(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('clean', tmpdir) + exit_code, __ = run_analyzer(tmpdir, cdb, []) + self.assertFalse(exit_code) + + def test_regular_sets_exit_code_if_asked(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, __ = run_analyzer(tmpdir, cdb, ['--status-bugs']) + self.assertTrue(exit_code) + + def test_clear_does_not_set_exit_code_if_asked(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('clean', tmpdir) + exit_code, __ = run_analyzer(tmpdir, cdb, ['--status-bugs']) + self.assertFalse(exit_code) + + def test_regular_sets_exit_code_if_asked_from_plist(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, __ = run_analyzer( + tmpdir, cdb, ['--status-bugs', '--plist']) + self.assertTrue(exit_code) + + def test_clear_does_not_set_exit_code_if_asked_from_plist(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('clean', tmpdir) + exit_code, __ = run_analyzer( + tmpdir, cdb, ['--status-bugs', '--plist']) + self.assertFalse(exit_code) + + +class OutputFormatTest(unittest.TestCase): + @staticmethod + def get_html_count(directory): + return len(glob.glob(os.path.join(directory, 'report-*.html'))) + + @staticmethod + def get_plist_count(directory): + return len(glob.glob(os.path.join(directory, 'report-*.plist'))) + + def test_default_creates_html_report(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, []) + self.assertTrue( + os.path.exists(os.path.join(reportdir, 'index.html'))) + self.assertEqual(self.get_html_count(reportdir), 2) + self.assertEqual(self.get_plist_count(reportdir), 0) + + def test_plist_and_html_creates_html_report(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist-html']) + self.assertTrue( + os.path.exists(os.path.join(reportdir, 'index.html'))) + self.assertEqual(self.get_html_count(reportdir), 2) + self.assertEqual(self.get_plist_count(reportdir), 5) + + def test_plist_does_not_creates_html_report(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist']) + self.assertFalse( + os.path.exists(os.path.join(reportdir, 'index.html'))) + self.assertEqual(self.get_html_count(reportdir), 0) + self.assertEqual(self.get_plist_count(reportdir), 5) + + +class FailureReportTest(unittest.TestCase): + def test_broken_creates_failure_reports(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('broken', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, []) + self.assertTrue( + os.path.isdir(os.path.join(reportdir, 'failures'))) + + def test_broken_does_not_creates_failure_reports(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('broken', tmpdir) + exit_code, reportdir = run_analyzer( + tmpdir, cdb, ['--no-failure-reports']) + self.assertFalse( + os.path.isdir(os.path.join(reportdir, 'failures'))) + + +class TitleTest(unittest.TestCase): + def assertTitleEqual(self, directory, expected): + import re + patterns = [ + re.compile(r'<title>(?P<page>.*)</title>'), + re.compile(r'<h1>(?P<head>.*)</h1>') + ] + result = dict() + + index = os.path.join(directory, 'index.html') + with open(index, 'r') as handler: + for line in handler.readlines(): + for regex in patterns: + match = regex.match(line.strip()) + if match: + result.update(match.groupdict()) + break + self.assertEqual(result['page'], result['head']) + self.assertEqual(result['page'], expected) + + def test_default_title_in_report(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('broken', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, []) + self.assertTitleEqual(reportdir, 'src - analyzer results') + + def test_given_title_in_report(self): + with fixtures.TempDir() as tmpdir: + cdb = prepare_cdb('broken', tmpdir) + exit_code, reportdir = run_analyzer( + tmpdir, cdb, ['--html-title', 'this is the title']) + self.assertTitleEqual(reportdir, 'this is the title') diff --git a/tools/scan-build-py/tests/functional/cases/test_from_cmd.py b/tools/scan-build-py/tests/functional/cases/test_from_cmd.py new file mode 100644 index 000000000000..fe7ecf69915b --- /dev/null +++ b/tools/scan-build-py/tests/functional/cases/test_from_cmd.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +from ...unit import fixtures +from . import make_args, check_call_and_report, create_empty_file +import unittest + +import os +import os.path +import glob + + +class OutputDirectoryTest(unittest.TestCase): + + @staticmethod + def run_analyzer(outdir, args, cmd): + return check_call_and_report( + ['scan-build', '--intercept-first', '-o', outdir] + args, + cmd) + + def test_regular_keeps_report_dir(self): + with fixtures.TempDir() as tmpdir: + make = make_args(tmpdir) + ['build_regular'] + outdir = self.run_analyzer(tmpdir, [], make) + self.assertTrue(os.path.isdir(outdir)) + + def test_clear_deletes_report_dir(self): + with fixtures.TempDir() as tmpdir: + make = make_args(tmpdir) + ['build_clean'] + outdir = self.run_analyzer(tmpdir, [], make) + self.assertFalse(os.path.isdir(outdir)) + + def test_clear_keeps_report_dir_when_asked(self): + with fixtures.TempDir() as tmpdir: + make = make_args(tmpdir) + ['build_clean'] + outdir = self.run_analyzer(tmpdir, ['--keep-empty'], make) + self.assertTrue(os.path.isdir(outdir)) + + +class RunAnalyzerTest(unittest.TestCase): + + @staticmethod + def get_plist_count(directory): + return len(glob.glob(os.path.join(directory, 'report-*.plist'))) + + def test_interposition_works(self): + with fixtures.TempDir() as tmpdir: + make = make_args(tmpdir) + ['build_regular'] + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--override-compiler'], + make) + + self.assertTrue(os.path.isdir(outdir)) + self.assertEqual(self.get_plist_count(outdir), 5) + + def test_intercept_wrapper_works(self): + with fixtures.TempDir() as tmpdir: + make = make_args(tmpdir) + ['build_regular'] + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--intercept-first', + '--override-compiler'], + make) + + self.assertTrue(os.path.isdir(outdir)) + self.assertEqual(self.get_plist_count(outdir), 5) + + def test_intercept_library_works(self): + with fixtures.TempDir() as tmpdir: + make = make_args(tmpdir) + ['build_regular'] + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--intercept-first'], + make) + + self.assertTrue(os.path.isdir(outdir)) + self.assertEqual(self.get_plist_count(outdir), 5) + + @staticmethod + def compile_empty_source_file(target_dir, is_cxx): + compiler = '$CXX' if is_cxx else '$CC' + src_file_name = 'test.cxx' if is_cxx else 'test.c' + src_file = os.path.join(target_dir, src_file_name) + obj_file = os.path.join(target_dir, 'test.o') + create_empty_file(src_file) + command = ' '.join([compiler, '-c', src_file, '-o', obj_file]) + return ['sh', '-c', command] + + def test_interposition_cc_works(self): + with fixtures.TempDir() as tmpdir: + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--override-compiler'], + self.compile_empty_source_file(tmpdir, False)) + self.assertEqual(self.get_plist_count(outdir), 1) + + def test_interposition_cxx_works(self): + with fixtures.TempDir() as tmpdir: + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--override-compiler'], + self.compile_empty_source_file(tmpdir, True)) + self.assertEqual(self.get_plist_count(outdir), 1) + + def test_intercept_cc_works(self): + with fixtures.TempDir() as tmpdir: + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--override-compiler', + '--intercept-first'], + self.compile_empty_source_file(tmpdir, False)) + self.assertEqual(self.get_plist_count(outdir), 1) + + def test_intercept_cxx_works(self): + with fixtures.TempDir() as tmpdir: + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--override-compiler', + '--intercept-first'], + self.compile_empty_source_file(tmpdir, True)) + self.assertEqual(self.get_plist_count(outdir), 1) diff --git a/tools/scan-build-py/tests/functional/exec/CMakeLists.txt b/tools/scan-build-py/tests/functional/exec/CMakeLists.txt new file mode 100644 index 000000000000..6e5d2e966184 --- /dev/null +++ b/tools/scan-build-py/tests/functional/exec/CMakeLists.txt @@ -0,0 +1,32 @@ +project(exec C) + +cmake_minimum_required(VERSION 2.8) + +include(CheckCCompilerFlag) +check_c_compiler_flag("-std=c99" C99_SUPPORTED) +if (C99_SUPPORTED) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") +endif() + +include(CheckFunctionExists) +include(CheckSymbolExists) + +add_definitions(-D_GNU_SOURCE) +list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) + +check_function_exists(execve HAVE_EXECVE) +check_function_exists(execv HAVE_EXECV) +check_function_exists(execvpe HAVE_EXECVPE) +check_function_exists(execvp HAVE_EXECVP) +check_function_exists(execvP HAVE_EXECVP2) +check_function_exists(exect HAVE_EXECT) +check_function_exists(execl HAVE_EXECL) +check_function_exists(execlp HAVE_EXECLP) +check_function_exists(execle HAVE_EXECLE) +check_function_exists(posix_spawn HAVE_POSIX_SPAWN) +check_function_exists(posix_spawnp HAVE_POSIX_SPAWNP) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(exec main.c) diff --git a/tools/scan-build-py/tests/functional/exec/config.h.in b/tools/scan-build-py/tests/functional/exec/config.h.in new file mode 100644 index 000000000000..6221083fd2cc --- /dev/null +++ b/tools/scan-build-py/tests/functional/exec/config.h.in @@ -0,0 +1,20 @@ +/* -*- coding: utf-8 -*- +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +*/ + +#pragma once + +#cmakedefine HAVE_EXECVE +#cmakedefine HAVE_EXECV +#cmakedefine HAVE_EXECVPE +#cmakedefine HAVE_EXECVP +#cmakedefine HAVE_EXECVP2 +#cmakedefine HAVE_EXECT +#cmakedefine HAVE_EXECL +#cmakedefine HAVE_EXECLP +#cmakedefine HAVE_EXECLE +#cmakedefine HAVE_POSIX_SPAWN +#cmakedefine HAVE_POSIX_SPAWNP diff --git a/tools/scan-build-py/tests/functional/exec/main.c b/tools/scan-build-py/tests/functional/exec/main.c new file mode 100644 index 000000000000..830cf3749cbd --- /dev/null +++ b/tools/scan-build-py/tests/functional/exec/main.c @@ -0,0 +1,307 @@ +/* -*- coding: utf-8 -*- +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +*/ + +#include "config.h" + +#include <sys/wait.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <paths.h> + +#if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP +#include <spawn.h> +#endif + +// ..:: environment access fixer - begin ::.. +#ifdef HAVE_NSGETENVIRON +#include <crt_externs.h> +#else +extern char **environ; +#endif + +char **get_environ() { +#ifdef HAVE_NSGETENVIRON + return *_NSGetEnviron(); +#else + return environ; +#endif +} +// ..:: environment access fixer - end ::.. + +// ..:: test fixtures - begin ::.. +static char const *cwd = NULL; +static FILE *fd = NULL; +static int need_comma = 0; + +void expected_out_open(const char *expected) { + cwd = getcwd(NULL, 0); + fd = fopen(expected, "w"); + if (!fd) { + perror("fopen"); + exit(EXIT_FAILURE); + } + fprintf(fd, "[\n"); + need_comma = 0; +} + +void expected_out_close() { + fprintf(fd, "]\n"); + fclose(fd); + fd = NULL; + + free((void *)cwd); + cwd = NULL; +} + +void expected_out(const char *file) { + if (need_comma) + fprintf(fd, ",\n"); + else + need_comma = 1; + + fprintf(fd, "{\n"); + fprintf(fd, " \"directory\": \"%s\",\n", cwd); + fprintf(fd, " \"command\": \"cc -c %s\",\n", file); + fprintf(fd, " \"file\": \"%s/%s\"\n", cwd, file); + fprintf(fd, "}\n"); +} + +void create_source(char *file) { + FILE *fd = fopen(file, "w"); + if (!fd) { + perror("fopen"); + exit(EXIT_FAILURE); + } + fprintf(fd, "typedef int score;\n"); + fclose(fd); +} + +typedef void (*exec_fun)(); + +void wait_for(pid_t child) { + int status; + if (-1 == waitpid(child, &status, 0)) { + perror("wait"); + exit(EXIT_FAILURE); + } + if (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE) { + fprintf(stderr, "children process has non zero exit code\n"); + exit(EXIT_FAILURE); + } +} + +#define FORK(FUNC) \ + { \ + pid_t child = fork(); \ + if (-1 == child) { \ + perror("fork"); \ + exit(EXIT_FAILURE); \ + } else if (0 == child) { \ + FUNC fprintf(stderr, "children process failed to exec\n"); \ + exit(EXIT_FAILURE); \ + } else { \ + wait_for(child); \ + } \ + } +// ..:: test fixtures - end ::.. + +#ifdef HAVE_EXECV +void call_execv() { + char *const file = "execv.c"; + char *const compiler = "/usr/bin/cc"; + char *const argv[] = {"cc", "-c", file, 0}; + + expected_out(file); + create_source(file); + + FORK(execv(compiler, argv);) +} +#endif + +#ifdef HAVE_EXECVE +void call_execve() { + char *const file = "execve.c"; + char *const compiler = "/usr/bin/cc"; + char *const argv[] = {compiler, "-c", file, 0}; + char *const envp[] = {"THIS=THAT", 0}; + + expected_out(file); + create_source(file); + + FORK(execve(compiler, argv, envp);) +} +#endif + +#ifdef HAVE_EXECVP +void call_execvp() { + char *const file = "execvp.c"; + char *const compiler = "cc"; + char *const argv[] = {compiler, "-c", file, 0}; + + expected_out(file); + create_source(file); + + FORK(execvp(compiler, argv);) +} +#endif + +#ifdef HAVE_EXECVP2 +void call_execvP() { + char *const file = "execv_p.c"; + char *const compiler = "cc"; + char *const argv[] = {compiler, "-c", file, 0}; + + expected_out(file); + create_source(file); + + FORK(execvP(compiler, _PATH_DEFPATH, argv);) +} +#endif + +#ifdef HAVE_EXECVPE +void call_execvpe() { + char *const file = "execvpe.c"; + char *const compiler = "cc"; + char *const argv[] = {"/usr/bin/cc", "-c", file, 0}; + char *const envp[] = {"THIS=THAT", 0}; + + expected_out(file); + create_source(file); + + FORK(execvpe(compiler, argv, envp);) +} +#endif + +#ifdef HAVE_EXECT +void call_exect() { + char *const file = "exect.c"; + char *const compiler = "/usr/bin/cc"; + char *const argv[] = {compiler, "-c", file, 0}; + char *const envp[] = {"THIS=THAT", 0}; + + expected_out(file); + create_source(file); + + FORK(exect(compiler, argv, envp);) +} +#endif + +#ifdef HAVE_EXECL +void call_execl() { + char *const file = "execl.c"; + char *const compiler = "/usr/bin/cc"; + + expected_out(file); + create_source(file); + + FORK(execl(compiler, "cc", "-c", file, (char *)0);) +} +#endif + +#ifdef HAVE_EXECLP +void call_execlp() { + char *const file = "execlp.c"; + char *const compiler = "cc"; + + expected_out(file); + create_source(file); + + FORK(execlp(compiler, compiler, "-c", file, (char *)0);) +} +#endif + +#ifdef HAVE_EXECLE +void call_execle() { + char *const file = "execle.c"; + char *const compiler = "/usr/bin/cc"; + char *const envp[] = {"THIS=THAT", 0}; + + expected_out(file); + create_source(file); + + FORK(execle(compiler, compiler, "-c", file, (char *)0, envp);) +} +#endif + +#ifdef HAVE_POSIX_SPAWN +void call_posix_spawn() { + char *const file = "posix_spawn.c"; + char *const compiler = "cc"; + char *const argv[] = {compiler, "-c", file, 0}; + + expected_out(file); + create_source(file); + + pid_t child; + if (0 != posix_spawn(&child, "/usr/bin/cc", 0, 0, argv, get_environ())) { + perror("posix_spawn"); + exit(EXIT_FAILURE); + } + wait_for(child); +} +#endif + +#ifdef HAVE_POSIX_SPAWNP +void call_posix_spawnp() { + char *const file = "posix_spawnp.c"; + char *const compiler = "cc"; + char *const argv[] = {compiler, "-c", file, 0}; + + expected_out(file); + create_source(file); + + pid_t child; + if (0 != posix_spawnp(&child, "cc", 0, 0, argv, get_environ())) { + perror("posix_spawnp"); + exit(EXIT_FAILURE); + } + wait_for(child); +} +#endif + +int main(int argc, char *const argv[]) { + if (argc != 2) + exit(EXIT_FAILURE); + + expected_out_open(argv[1]); +#ifdef HAVE_EXECV + call_execv(); +#endif +#ifdef HAVE_EXECVE + call_execve(); +#endif +#ifdef HAVE_EXECVP + call_execvp(); +#endif +#ifdef HAVE_EXECVP2 + call_execvP(); +#endif +#ifdef HAVE_EXECVPE + call_execvpe(); +#endif +#ifdef HAVE_EXECT + call_exect(); +#endif +#ifdef HAVE_EXECL + call_execl(); +#endif +#ifdef HAVE_EXECLP + call_execlp(); +#endif +#ifdef HAVE_EXECLE + call_execle(); +#endif +#ifdef HAVE_POSIX_SPAWN + call_posix_spawn(); +#endif +#ifdef HAVE_POSIX_SPAWNP + call_posix_spawnp(); +#endif + expected_out_close(); + return 0; +} diff --git a/tools/scan-build-py/tests/functional/src/broken-one.c b/tools/scan-build-py/tests/functional/src/broken-one.c new file mode 100644 index 000000000000..f0550238132c --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/broken-one.c @@ -0,0 +1,6 @@ +#include <notexisting.hpp> + +int value(int in) +{ + return 2 * in; +} diff --git a/tools/scan-build-py/tests/functional/src/broken-two.c b/tools/scan-build-py/tests/functional/src/broken-two.c new file mode 100644 index 000000000000..7b4c12ff5c39 --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/broken-two.c @@ -0,0 +1 @@ +int test() { ; diff --git a/tools/scan-build-py/tests/functional/src/build/Makefile b/tools/scan-build-py/tests/functional/src/build/Makefile new file mode 100644 index 000000000000..a8c0aafd0e5e --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/build/Makefile @@ -0,0 +1,42 @@ +SRCDIR := .. +OBJDIR := . + +CFLAGS = -Wall -DDEBUG -Dvariable="value with space" -I $(SRCDIR)/include +LDFLAGS = +PROGRAM = $(OBJDIR)/prg + +$(OBJDIR)/main.o: $(SRCDIR)/main.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/main.c + +$(OBJDIR)/clean-one.o: $(SRCDIR)/clean-one.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/clean-one.c + +$(OBJDIR)/clean-two.o: $(SRCDIR)/clean-two.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/clean-two.c + +$(OBJDIR)/emit-one.o: $(SRCDIR)/emit-one.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/emit-one.c + +$(OBJDIR)/emit-two.o: $(SRCDIR)/emit-two.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/emit-two.c + +$(OBJDIR)/broken-one.o: $(SRCDIR)/broken-one.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/broken-one.c + +$(OBJDIR)/broken-two.o: $(SRCDIR)/broken-two.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/broken-two.c + +$(PROGRAM): $(OBJDIR)/main.o $(OBJDIR)/clean-one.o $(OBJDIR)/clean-two.o $(OBJDIR)/emit-one.o $(OBJDIR)/emit-two.o + $(CC) $(LDFLAGS) -o $@ $(OBJDIR)/main.o $(OBJDIR)/clean-one.o $(OBJDIR)/clean-two.o $(OBJDIR)/emit-one.o $(OBJDIR)/emit-two.o + +build_regular: $(PROGRAM) + +build_clean: $(OBJDIR)/main.o $(OBJDIR)/clean-one.o $(OBJDIR)/clean-two.o + +build_broken: $(OBJDIR)/main.o $(OBJDIR)/broken-one.o $(OBJDIR)/broken-two.o + +build_all_in_one: $(SRCDIR)/main.c $(SRCDIR)/clean-one.c $(SRCDIR)/clean-two.c $(SRCDIR)/emit-one.c $(SRCDIR)/emit-two.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROGRAM) $(SRCDIR)/main.c $(SRCDIR)/clean-one.c $(SRCDIR)/clean-two.c $(SRCDIR)/emit-one.c $(SRCDIR)/emit-two.c + +clean: + rm -f $(PROGRAM) $(OBJDIR)/*.o diff --git a/tools/scan-build-py/tests/functional/src/clean-one.c b/tools/scan-build-py/tests/functional/src/clean-one.c new file mode 100644 index 000000000000..08c5f33609bb --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/clean-one.c @@ -0,0 +1,13 @@ +#include <clean-one.h> + +int do_nothing_loop() +{ + int i = 32; + int idx = 0; + + for (idx = i; idx > 0; --idx) + { + i += idx; + } + return i; +} diff --git a/tools/scan-build-py/tests/functional/src/clean-two.c b/tools/scan-build-py/tests/functional/src/clean-two.c new file mode 100644 index 000000000000..73bc288627d0 --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/clean-two.c @@ -0,0 +1,11 @@ +#include <clean-one.h> + +#include <stdlib.h> + +unsigned int another_method() +{ + unsigned int const size = do_nothing_loop(); + unsigned int const square = size * size; + + return square; +} diff --git a/tools/scan-build-py/tests/functional/src/compilation_database/build_broken.json.in b/tools/scan-build-py/tests/functional/src/compilation_database/build_broken.json.in new file mode 100644 index 000000000000..104a4191cb1b --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/compilation_database/build_broken.json.in @@ -0,0 +1,43 @@ +[ +{ + "directory": "${path}", + "command": "g++ -c -o main.o main.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/main.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o broken-one.o broken-one.c -Wall -DDEBUG \"-Dvariable=value with space\"", + "file": "${path}/broken-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o broken-two.o broken-two.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/broken-two.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o clean-one.o clean-one.c -Wall -DDEBUG \"-Dvariable=value with space\" -Iinclude", + "file": "${path}/clean-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o clean-two.o clean-two.c -Wall -DDEBUG -Dvariable=value -I ./include", + "file": "${path}/clean-two.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o emit-one.o emit-one.c -Wall -DDEBUG \"-Dvariable=value with space\"", + "file": "${path}/emit-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o emit-two.o emit-two.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/emit-two.c" +} +] diff --git a/tools/scan-build-py/tests/functional/src/compilation_database/build_clean.json.in b/tools/scan-build-py/tests/functional/src/compilation_database/build_clean.json.in new file mode 100644 index 000000000000..aa4dcde8e5e7 --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/compilation_database/build_clean.json.in @@ -0,0 +1,19 @@ +[ +{ + "directory": "${path}", + "command": "g++ -c -o main.o main.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/main.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o clean-one.o clean-one.c -Wall -DDEBUG \"-Dvariable=value with space\" -Iinclude", + "file": "${path}/clean-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o clean-two.o clean-two.c -Wall -DDEBUG -Dvariable=value -I ./include", + "file": "${path}/clean-two.c" +} +] diff --git a/tools/scan-build-py/tests/functional/src/compilation_database/build_regular.json.in b/tools/scan-build-py/tests/functional/src/compilation_database/build_regular.json.in new file mode 100644 index 000000000000..0200c1d8624a --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/compilation_database/build_regular.json.in @@ -0,0 +1,31 @@ +[ +{ + "directory": "${path}", + "command": "g++ -c -o main.o main.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/main.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o clean-one.o clean-one.c -Wall -DDEBUG \"-Dvariable=value with space\" -Iinclude", + "file": "${path}/clean-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o clean-two.o clean-two.c -Wall -DDEBUG -Dvariable=value -I ./include", + "file": "${path}/clean-two.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o emit-one.o emit-one.c -Wall -DDEBUG \"-Dvariable=value with space\"", + "file": "${path}/emit-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o emit-two.o emit-two.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/emit-two.c" +} +] diff --git a/tools/scan-build-py/tests/functional/src/emit-one.c b/tools/scan-build-py/tests/functional/src/emit-one.c new file mode 100644 index 000000000000..6cbd9cea72b9 --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/emit-one.c @@ -0,0 +1,23 @@ +#include <assert.h> + +int div(int numerator, int denominator) +{ + return numerator / denominator; +} + +void div_test() +{ + int i = 0; + for (i = 0; i < 2; ++i) + assert(div(2 * i, i) == 2); +} + +int do_nothing() +{ + unsigned int i = 0; + + int k = 100; + int j = k + 1; + + return j; +} diff --git a/tools/scan-build-py/tests/functional/src/emit-two.c b/tools/scan-build-py/tests/functional/src/emit-two.c new file mode 100644 index 000000000000..faea77167f4c --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/emit-two.c @@ -0,0 +1,13 @@ + +int bad_guy(int * i) +{ + *i = 9; + return *i; +} + +void bad_guy_test() +{ + int * ptr = 0; + + bad_guy(ptr); +} diff --git a/tools/scan-build-py/tests/functional/src/include/clean-one.h b/tools/scan-build-py/tests/functional/src/include/clean-one.h new file mode 100644 index 000000000000..695dbd04c658 --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/include/clean-one.h @@ -0,0 +1,6 @@ +#ifndef CLEAN_ONE_H +#define CLEAN_ONE_H + +int do_nothing_loop(); + +#endif diff --git a/tools/scan-build-py/tests/functional/src/main.c b/tools/scan-build-py/tests/functional/src/main.c new file mode 100644 index 000000000000..905869dfa380 --- /dev/null +++ b/tools/scan-build-py/tests/functional/src/main.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/tools/scan-build-py/tests/unit/__init__.py b/tools/scan-build-py/tests/unit/__init__.py new file mode 100644 index 000000000000..4fa9edc0fff1 --- /dev/null +++ b/tools/scan-build-py/tests/unit/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +from . import test_command +from . import test_clang +from . import test_runner +from . import test_report +from . import test_analyze +from . import test_intercept +from . import test_shell + + +def load_tests(loader, suite, pattern): + suite.addTests(loader.loadTestsFromModule(test_command)) + suite.addTests(loader.loadTestsFromModule(test_clang)) + suite.addTests(loader.loadTestsFromModule(test_runner)) + suite.addTests(loader.loadTestsFromModule(test_report)) + suite.addTests(loader.loadTestsFromModule(test_analyze)) + suite.addTests(loader.loadTestsFromModule(test_intercept)) + suite.addTests(loader.loadTestsFromModule(test_shell)) + return suite diff --git a/tools/scan-build-py/tests/unit/fixtures.py b/tools/scan-build-py/tests/unit/fixtures.py new file mode 100644 index 000000000000..d80f5e64774c --- /dev/null +++ b/tools/scan-build-py/tests/unit/fixtures.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import contextlib +import tempfile +import shutil +import unittest + + +class Spy(object): + def __init__(self): + self.arg = None + self.success = 0 + + def call(self, params): + self.arg = params + return self.success + + +@contextlib.contextmanager +def TempDir(): + name = tempfile.mkdtemp(prefix='scan-build-test-') + try: + yield name + finally: + shutil.rmtree(name) + + +class TestCase(unittest.TestCase): + def assertIn(self, element, collection): + found = False + for it in collection: + if element == it: + found = True + + self.assertTrue(found, '{0} does not have {1}'.format(collection, + element)) diff --git a/tools/scan-build-py/tests/unit/test_analyze.py b/tools/scan-build-py/tests/unit/test_analyze.py new file mode 100644 index 000000000000..b77db4818024 --- /dev/null +++ b/tools/scan-build-py/tests/unit/test_analyze.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import libscanbuild.analyze as sut +from . import fixtures diff --git a/tools/scan-build-py/tests/unit/test_clang.py b/tools/scan-build-py/tests/unit/test_clang.py new file mode 100644 index 000000000000..2f1fd79d4a92 --- /dev/null +++ b/tools/scan-build-py/tests/unit/test_clang.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import libscanbuild.clang as sut +from . import fixtures +import os.path + + +class GetClangArgumentsTest(fixtures.TestCase): + def test_get_clang_arguments(self): + with fixtures.TempDir() as tmpdir: + filename = os.path.join(tmpdir, 'test.c') + with open(filename, 'w') as handle: + handle.write('') + + result = sut.get_arguments( + ['clang', '-c', filename, '-DNDEBUG', '-Dvar="this is it"'], + tmpdir) + + self.assertIn('NDEBUG', result) + self.assertIn('var="this is it"', result) + + def test_get_clang_arguments_fails(self): + self.assertRaises( + Exception, sut.get_arguments, + ['clang', '-###', '-fsyntax-only', '-x', 'c', 'notexist.c'], '.') + + +class GetCheckersTest(fixtures.TestCase): + def test_get_checkers(self): + # this test is only to see is not crashing + result = sut.get_checkers('clang', []) + self.assertTrue(len(result)) + + def test_get_active_checkers(self): + # this test is only to see is not crashing + result = sut.get_active_checkers('clang', []) + self.assertTrue(len(result)) diff --git a/tools/scan-build-py/tests/unit/test_command.py b/tools/scan-build-py/tests/unit/test_command.py new file mode 100644 index 000000000000..9a6aae65c605 --- /dev/null +++ b/tools/scan-build-py/tests/unit/test_command.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import libscanbuild.command as sut +from . import fixtures +import unittest + + +class ParseTest(unittest.TestCase): + + def test_action(self): + def test(expected, cmd): + opts = sut.classify_parameters(cmd) + self.assertEqual(expected, opts['action']) + + Link = sut.Action.Link + test(Link, ['clang', 'source.c']) + + Compile = sut.Action.Compile + test(Compile, ['clang', '-c', 'source.c']) + test(Compile, ['clang', '-c', 'source.c', '-MF', 'source.d']) + + Preprocess = sut.Action.Ignored + test(Preprocess, ['clang', '-E', 'source.c']) + test(Preprocess, ['clang', '-c', '-E', 'source.c']) + test(Preprocess, ['clang', '-c', '-M', 'source.c']) + test(Preprocess, ['clang', '-c', '-MM', 'source.c']) + + def test_optimalizations(self): + def test(cmd): + opts = sut.classify_parameters(cmd) + return opts.get('compile_options', []) + + self.assertEqual(['-O'], test(['clang', '-c', 'source.c', '-O'])) + self.assertEqual(['-O1'], test(['clang', '-c', 'source.c', '-O1'])) + self.assertEqual(['-Os'], test(['clang', '-c', 'source.c', '-Os'])) + self.assertEqual(['-O2'], test(['clang', '-c', 'source.c', '-O2'])) + self.assertEqual(['-O3'], test(['clang', '-c', 'source.c', '-O3'])) + + def test_language(self): + def test(cmd): + opts = sut.classify_parameters(cmd) + return opts.get('language') + + self.assertEqual(None, test(['clang', '-c', 'source.c'])) + self.assertEqual('c', test(['clang', '-c', 'source.c', '-x', 'c'])) + self.assertEqual('cpp', test(['clang', '-c', 'source.c', '-x', 'cpp'])) + + def test_output(self): + def test(cmd): + opts = sut.classify_parameters(cmd) + return opts.get('output') + + self.assertEqual(None, test(['clang', '-c', 'source.c'])) + self.assertEqual('source.o', + test(['clang', '-c', '-o', 'source.o', 'source.c'])) + + def test_arch(self): + def test(cmd): + opts = sut.classify_parameters(cmd) + return opts.get('archs_seen', []) + + eq = self.assertEqual + + eq([], test(['clang', '-c', 'source.c'])) + eq(['mips'], + test(['clang', '-c', 'source.c', '-arch', 'mips'])) + eq(['mips', 'i386'], + test(['clang', '-c', 'source.c', '-arch', 'mips', '-arch', 'i386'])) + + def test_input_file(self): + def test(cmd): + opts = sut.classify_parameters(cmd) + return opts.get('files', []) + + eq = self.assertEqual + + eq(['src.c'], test(['clang', 'src.c'])) + eq(['src.c'], test(['clang', '-c', 'src.c'])) + eq(['s1.c', 's2.c'], test(['clang', '-c', 's1.c', 's2.c'])) + + def test_include(self): + def test(cmd): + opts = sut.classify_parameters(cmd) + return opts.get('compile_options', []) + + eq = self.assertEqual + + eq([], test(['clang', '-c', 'src.c'])) + eq(['-include', '/usr/local/include'], + test(['clang', '-c', 'src.c', '-include', '/usr/local/include'])) + eq(['-I.'], + test(['clang', '-c', 'src.c', '-I.'])) + eq(['-I', '.'], + test(['clang', '-c', 'src.c', '-I', '.'])) + eq(['-I/usr/local/include'], + test(['clang', '-c', 'src.c', '-I/usr/local/include'])) + eq(['-I', '/usr/local/include'], + test(['clang', '-c', 'src.c', '-I', '/usr/local/include'])) + eq(['-I/opt', '-I', '/opt/otp/include'], + test(['clang', '-c', 'src.c', '-I/opt', '-I', '/opt/otp/include'])) + eq(['-isystem', '/path'], + test(['clang', '-c', 'src.c', '-isystem', '/path'])) + eq(['-isystem=/path'], + test(['clang', '-c', 'src.c', '-isystem=/path'])) + + def test_define(self): + def test(cmd): + opts = sut.classify_parameters(cmd) + return opts.get('compile_options', []) + + eq = self.assertEqual + + eq([], test(['clang', '-c', 'src.c'])) + eq(['-DNDEBUG'], + test(['clang', '-c', 'src.c', '-DNDEBUG'])) + eq(['-UNDEBUG'], + test(['clang', '-c', 'src.c', '-UNDEBUG'])) + eq(['-Dvar1=val1', '-Dvar2=val2'], + test(['clang', '-c', 'src.c', '-Dvar1=val1', '-Dvar2=val2'])) + eq(['-Dvar="val ues"'], + test(['clang', '-c', 'src.c', '-Dvar="val ues"'])) + + def test_ignored_flags(self): + def test(flags): + cmd = ['clang', 'src.o'] + opts = sut.classify_parameters(cmd + flags) + self.assertEqual(['src.o'], opts.get('compile_options')) + + test([]) + test(['-lrt', '-L/opt/company/lib']) + test(['-static']) + test(['-Wnoexcept', '-Wall']) + test(['-mtune=i386', '-mcpu=i386']) + + def test_compile_only_flags(self): + def test(cmd): + opts = sut.classify_parameters(cmd) + return opts.get('compile_options', []) + + eq = self.assertEqual + + eq(['-std=C99'], + test(['clang', '-c', 'src.c', '-std=C99'])) + eq(['-nostdinc'], + test(['clang', '-c', 'src.c', '-nostdinc'])) + eq(['-isystem', '/image/debian'], + test(['clang', '-c', 'src.c', '-isystem', '/image/debian'])) + eq(['-iprefix', '/usr/local'], + test(['clang', '-c', 'src.c', '-iprefix', '/usr/local'])) + eq(['-iquote=me'], + test(['clang', '-c', 'src.c', '-iquote=me'])) + eq(['-iquote', 'me'], + test(['clang', '-c', 'src.c', '-iquote', 'me'])) + + def test_compile_and_link_flags(self): + def test(cmd): + opts = sut.classify_parameters(cmd) + return opts.get('compile_options', []) + + eq = self.assertEqual + + eq(['-fsinged-char'], + test(['clang', '-c', 'src.c', '-fsinged-char'])) + eq(['-fPIC'], + test(['clang', '-c', 'src.c', '-fPIC'])) + eq(['-stdlib=libc++'], + test(['clang', '-c', 'src.c', '-stdlib=libc++'])) + eq(['--sysroot', '/'], + test(['clang', '-c', 'src.c', '--sysroot', '/'])) + eq(['-isysroot', '/'], + test(['clang', '-c', 'src.c', '-isysroot', '/'])) + eq([], + test(['clang', '-c', 'src.c', '-fsyntax-only'])) + eq([], + test(['clang', '-c', 'src.c', '-sectorder', 'a', 'b', 'c'])) + + def test_detect_cxx_from_compiler_name(self): + def test(cmd): + opts = sut.classify_parameters(cmd) + return opts.get('c++') + + eq = self.assertEqual + + eq(False, test(['cc', '-c', 'src.c'])) + eq(True, test(['c++', '-c', 'src.c'])) + eq(False, test(['clang', '-c', 'src.c'])) + eq(True, test(['clang++', '-c', 'src.c'])) + eq(False, test(['gcc', '-c', 'src.c'])) + eq(True, test(['g++', '-c', 'src.c'])) diff --git a/tools/scan-build-py/tests/unit/test_intercept.py b/tools/scan-build-py/tests/unit/test_intercept.py new file mode 100644 index 000000000000..b6f01f36eeb3 --- /dev/null +++ b/tools/scan-build-py/tests/unit/test_intercept.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import libscanbuild.intercept as sut +from . import fixtures +import os.path + + +class InterceptUtilTest(fixtures.TestCase): + + def test_is_compiler_call_filter(self): + def test(command): + return sut.is_compiler_call({'command': [command]}) + + self.assertTrue(test('clang')) + self.assertTrue(test('clang-3.6')) + self.assertTrue(test('clang++')) + self.assertTrue(test('clang++-3.5.1')) + self.assertTrue(test('cc')) + self.assertTrue(test('c++')) + self.assertTrue(test('gcc')) + self.assertTrue(test('g++')) + self.assertTrue(test('/usr/local/bin/gcc')) + self.assertTrue(test('/usr/local/bin/g++')) + self.assertTrue(test('/usr/local/bin/clang')) + self.assertTrue(test('armv7_neno-linux-gnueabi-g++')) + + self.assertFalse(test('')) + self.assertFalse(test('ld')) + self.assertFalse(test('as')) + self.assertFalse(test('/usr/local/bin/compiler')) + + def test_format_entry_filters_action(self): + def test(command): + return list(sut.format_entry( + {'command': command, 'directory': '/opt/src/project'})) + + self.assertTrue(test(['cc', '-c', 'file.c', '-o', 'file.o'])) + self.assertFalse(test(['cc', '-E', 'file.c'])) + self.assertFalse(test(['cc', '-MM', 'file.c'])) + self.assertFalse(test(['cc', 'this.o', 'that.o', '-o', 'a.out'])) + self.assertFalse(test(['cc', '-print-prog-name'])) + + def test_format_entry_normalize_filename(self): + directory = os.path.join(os.sep, 'home', 'me', 'project') + + def test(command): + result = list(sut.format_entry( + {'command': command, 'directory': directory})) + return result[0]['file'] + + self.assertEqual(test(['cc', '-c', 'file.c']), + os.path.join(directory, 'file.c')) + self.assertEqual(test(['cc', '-c', './file.c']), + os.path.join(directory, 'file.c')) + self.assertEqual(test(['cc', '-c', '../file.c']), + os.path.join(os.path.dirname(directory), 'file.c')) + self.assertEqual(test(['cc', '-c', '/opt/file.c']), + '/opt/file.c') + + def test_sip(self): + def create_status_report(filename, message): + content = """#!/usr/bin/env sh + echo 'sa-la-la-la' + echo 'la-la-la' + echo '{0}' + echo 'sa-la-la-la' + echo 'la-la-la' + """.format(message) + lines = [line.strip() for line in content.split('\n')] + with open(filename, 'w') as handle: + handle.write('\n'.join(lines)) + handle.close() + os.chmod(filename, 0x1ff) + + def create_csrutil(dest_dir, status): + filename = os.path.join(dest_dir, 'csrutil') + message = 'System Integrity Protection status: {0}'.format(status) + return create_status_report(filename, message) + + def create_sestatus(dest_dir, status): + filename = os.path.join(dest_dir, 'sestatus') + message = 'SELinux status:\t{0}'.format(status) + return create_status_report(filename, message) + + ENABLED = 'enabled' + DISABLED = 'disabled' + + OSX = 'darwin' + LINUX = 'linux' + + with fixtures.TempDir() as tmpdir: + try: + saved = os.environ['PATH'] + os.environ['PATH'] = tmpdir + ':' + saved + + create_csrutil(tmpdir, ENABLED) + self.assertTrue(sut.is_preload_disabled(OSX)) + + create_csrutil(tmpdir, DISABLED) + self.assertFalse(sut.is_preload_disabled(OSX)) + + create_sestatus(tmpdir, ENABLED) + self.assertTrue(sut.is_preload_disabled(LINUX)) + + create_sestatus(tmpdir, DISABLED) + self.assertFalse(sut.is_preload_disabled(LINUX)) + finally: + os.environ['PATH'] = saved + + try: + saved = os.environ['PATH'] + os.environ['PATH'] = '' + # shall be false when it's not in the path + self.assertFalse(sut.is_preload_disabled(OSX)) + self.assertFalse(sut.is_preload_disabled(LINUX)) + + self.assertFalse(sut.is_preload_disabled('unix')) + finally: + os.environ['PATH'] = saved diff --git a/tools/scan-build-py/tests/unit/test_report.py b/tools/scan-build-py/tests/unit/test_report.py new file mode 100644 index 000000000000..d505afc20a89 --- /dev/null +++ b/tools/scan-build-py/tests/unit/test_report.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import libscanbuild.report as sut +from . import fixtures +import unittest +import os +import os.path + + +def run_bug_parse(content): + with fixtures.TempDir() as tmpdir: + file_name = os.path.join(tmpdir, 'test.html') + with open(file_name, 'w') as handle: + handle.writelines(content) + for bug in sut.parse_bug_html(file_name): + return bug + + +def run_crash_parse(content, preproc): + with fixtures.TempDir() as tmpdir: + file_name = os.path.join(tmpdir, preproc + '.info.txt') + with open(file_name, 'w') as handle: + handle.writelines(content) + return sut.parse_crash(file_name) + + +class ParseFileTest(unittest.TestCase): + + def test_parse_bug(self): + content = [ + "some header\n", + "<!-- BUGDESC Division by zero -->\n", + "<!-- BUGTYPE Division by zero -->\n", + "<!-- BUGCATEGORY Logic error -->\n", + "<!-- BUGFILE xx -->\n", + "<!-- BUGLINE 5 -->\n", + "<!-- BUGCOLUMN 22 -->\n", + "<!-- BUGPATHLENGTH 4 -->\n", + "<!-- BUGMETAEND -->\n", + "<!-- REPORTHEADER -->\n", + "some tails\n"] + result = run_bug_parse(content) + self.assertEqual(result['bug_category'], 'Logic error') + self.assertEqual(result['bug_path_length'], 4) + self.assertEqual(result['bug_line'], 5) + self.assertEqual(result['bug_description'], 'Division by zero') + self.assertEqual(result['bug_type'], 'Division by zero') + self.assertEqual(result['bug_file'], 'xx') + + def test_parse_bug_empty(self): + content = [] + result = run_bug_parse(content) + self.assertEqual(result['bug_category'], 'Other') + self.assertEqual(result['bug_path_length'], 1) + self.assertEqual(result['bug_line'], 0) + + def test_parse_crash(self): + content = [ + "/some/path/file.c\n", + "Some very serious Error\n", + "bla\n", + "bla-bla\n"] + result = run_crash_parse(content, 'file.i') + self.assertEqual(result['source'], content[0].rstrip()) + self.assertEqual(result['problem'], content[1].rstrip()) + self.assertEqual(os.path.basename(result['file']), + 'file.i') + self.assertEqual(os.path.basename(result['info']), + 'file.i.info.txt') + self.assertEqual(os.path.basename(result['stderr']), + 'file.i.stderr.txt') + + def test_parse_real_crash(self): + import libscanbuild.runner as sut2 + import re + with fixtures.TempDir() as tmpdir: + filename = os.path.join(tmpdir, 'test.c') + with open(filename, 'w') as handle: + handle.write('int main() { return 0') + # produce failure report + opts = {'directory': os.getcwd(), + 'clang': 'clang', + 'file': filename, + 'report': ['-fsyntax-only', '-E', filename], + 'language': 'c', + 'output_dir': tmpdir, + 'error_type': 'other_error', + 'error_output': 'some output', + 'exit_code': 13} + sut2.report_failure(opts) + # find the info file + pp_file = None + for root, _, files in os.walk(tmpdir): + keys = [os.path.join(root, name) for name in files] + for key in keys: + if re.match(r'^(.*/)+clang(.*)\.i$', key): + pp_file = key + self.assertIsNot(pp_file, None) + # read the failure report back + result = sut.parse_crash(pp_file + '.info.txt') + self.assertEqual(result['source'], filename) + self.assertEqual(result['problem'], 'Other Error') + self.assertEqual(result['file'], pp_file) + self.assertEqual(result['info'], pp_file + '.info.txt') + self.assertEqual(result['stderr'], pp_file + '.stderr.txt') + + +class ReportMethodTest(unittest.TestCase): + + def test_chop(self): + self.assertEqual('file', sut.chop('/prefix', '/prefix/file')) + self.assertEqual('file', sut.chop('/prefix/', '/prefix/file')) + self.assertEqual('lib/file', sut.chop('/prefix/', '/prefix/lib/file')) + self.assertEqual('/prefix/file', sut.chop('', '/prefix/file')) + + def test_chop_when_cwd(self): + self.assertEqual('../src/file', sut.chop('/cwd', '/src/file')) + self.assertEqual('../src/file', sut.chop('/prefix/cwd', + '/prefix/src/file')) + + +class GetPrefixFromCompilationDatabaseTest(fixtures.TestCase): + + def test_with_different_filenames(self): + self.assertEqual( + sut.commonprefix(['/tmp/a.c', '/tmp/b.c']), '/tmp') + + def test_with_different_dirnames(self): + self.assertEqual( + sut.commonprefix(['/tmp/abs/a.c', '/tmp/ack/b.c']), '/tmp') + + def test_no_common_prefix(self): + self.assertEqual( + sut.commonprefix(['/tmp/abs/a.c', '/usr/ack/b.c']), '/') + + def test_with_single_file(self): + self.assertEqual( + sut.commonprefix(['/tmp/a.c']), '/tmp') + + def test_empty(self): + self.assertEqual( + sut.commonprefix([]), '') diff --git a/tools/scan-build-py/tests/unit/test_runner.py b/tools/scan-build-py/tests/unit/test_runner.py new file mode 100644 index 000000000000..ea10051d8506 --- /dev/null +++ b/tools/scan-build-py/tests/unit/test_runner.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import libscanbuild.runner as sut +from . import fixtures +import unittest +import re +import os +import os.path + + +def run_analyzer(content, opts): + with fixtures.TempDir() as tmpdir: + filename = os.path.join(tmpdir, 'test.cpp') + with open(filename, 'w') as handle: + handle.write(content) + + opts.update({ + 'directory': os.getcwd(), + 'clang': 'clang', + 'file': filename, + 'language': 'c++', + 'analyze': ['--analyze', '-x', 'c++', filename], + 'output': ['-o', tmpdir]}) + spy = fixtures.Spy() + result = sut.run_analyzer(opts, spy.call) + return (result, spy.arg) + + +class RunAnalyzerTest(unittest.TestCase): + + def test_run_analyzer(self): + content = "int div(int n, int d) { return n / d; }" + (result, fwds) = run_analyzer(content, dict()) + self.assertEqual(None, fwds) + self.assertEqual(0, result['exit_code']) + + def test_run_analyzer_crash(self): + content = "int div(int n, int d) { return n / d }" + (result, fwds) = run_analyzer(content, dict()) + self.assertEqual(None, fwds) + self.assertEqual(1, result['exit_code']) + + def test_run_analyzer_crash_and_forwarded(self): + content = "int div(int n, int d) { return n / d }" + (_, fwds) = run_analyzer(content, {'output_failures': True}) + self.assertEqual('crash', fwds['error_type']) + self.assertEqual(1, fwds['exit_code']) + self.assertTrue(len(fwds['error_output']) > 0) + + +class SetAnalyzerOutputTest(fixtures.TestCase): + + def test_not_defined(self): + with fixtures.TempDir() as tmpdir: + opts = {'output_dir': tmpdir} + spy = fixtures.Spy() + sut.set_analyzer_output(opts, spy.call) + self.assertTrue(os.path.exists(spy.arg['output'][1])) + self.assertTrue(os.path.isdir(spy.arg['output'][1])) + + def test_html(self): + with fixtures.TempDir() as tmpdir: + opts = {'output_dir': tmpdir, 'output_format': 'html'} + spy = fixtures.Spy() + sut.set_analyzer_output(opts, spy.call) + self.assertTrue(os.path.exists(spy.arg['output'][1])) + self.assertTrue(os.path.isdir(spy.arg['output'][1])) + + def test_plist_html(self): + with fixtures.TempDir() as tmpdir: + opts = {'output_dir': tmpdir, 'output_format': 'plist-html'} + spy = fixtures.Spy() + sut.set_analyzer_output(opts, spy.call) + self.assertTrue(os.path.exists(spy.arg['output'][1])) + self.assertTrue(os.path.isfile(spy.arg['output'][1])) + + def test_plist(self): + with fixtures.TempDir() as tmpdir: + opts = {'output_dir': tmpdir, 'output_format': 'plist'} + spy = fixtures.Spy() + sut.set_analyzer_output(opts, spy.call) + self.assertTrue(os.path.exists(spy.arg['output'][1])) + self.assertTrue(os.path.isfile(spy.arg['output'][1])) + + +class ReportFailureTest(fixtures.TestCase): + + def assertUnderFailures(self, path): + self.assertEqual('failures', os.path.basename(os.path.dirname(path))) + + def test_report_failure_create_files(self): + with fixtures.TempDir() as tmpdir: + # create input file + filename = os.path.join(tmpdir, 'test.c') + with open(filename, 'w') as handle: + handle.write('int main() { return 0') + uname_msg = ' '.join(os.uname()) + os.linesep + error_msg = 'this is my error output' + # execute test + opts = {'directory': os.getcwd(), + 'clang': 'clang', + 'file': filename, + 'report': ['-fsyntax-only', '-E', filename], + 'language': 'c', + 'output_dir': tmpdir, + 'error_type': 'other_error', + 'error_output': error_msg, + 'exit_code': 13} + sut.report_failure(opts) + # verify the result + result = dict() + pp_file = None + for root, _, files in os.walk(tmpdir): + keys = [os.path.join(root, name) for name in files] + for key in keys: + with open(key, 'r') as handle: + result[key] = handle.readlines() + if re.match(r'^(.*/)+clang(.*)\.i$', key): + pp_file = key + + # prepocessor file generated + self.assertUnderFailures(pp_file) + # info file generated and content dumped + info_file = pp_file + '.info.txt' + self.assertIn(info_file, result) + self.assertEqual('Other Error\n', result[info_file][1]) + self.assertEqual(uname_msg, result[info_file][3]) + # error file generated and content dumped + error_file = pp_file + '.stderr.txt' + self.assertIn(error_file, result) + self.assertEqual([error_msg], result[error_file]) + + +class AnalyzerTest(unittest.TestCase): + + def test_set_language(self): + def test(expected, input): + spy = fixtures.Spy() + self.assertEqual(spy.success, sut.language_check(input, spy.call)) + self.assertEqual(expected, spy.arg['language']) + + l = 'language' + f = 'file' + i = 'c++' + test('c', {f: 'file.c', l: 'c', i: False}) + test('c++', {f: 'file.c', l: 'c++', i: False}) + test('c++', {f: 'file.c', i: True}) + test('c', {f: 'file.c', i: False}) + test('c++', {f: 'file.cxx', i: False}) + test('c-cpp-output', {f: 'file.i', i: False}) + test('c++-cpp-output', {f: 'file.i', i: True}) + test('c-cpp-output', {f: 'f.i', l: 'c-cpp-output', i: True}) + + def test_arch_loop(self): + def test(input): + spy = fixtures.Spy() + sut.arch_check(input, spy.call) + return spy.arg + + input = {'key': 'value'} + self.assertEqual(input, test(input)) + + input = {'archs_seen': ['i386']} + self.assertEqual({'arch': 'i386'}, test(input)) + + input = {'archs_seen': ['ppc']} + self.assertEqual(None, test(input)) + + input = {'archs_seen': ['i386', 'ppc']} + self.assertEqual({'arch': 'i386'}, test(input)) + + input = {'archs_seen': ['i386', 'sparc']} + result = test(input) + self.assertTrue(result == {'arch': 'i386'} or + result == {'arch': 'sparc'}) + + +@sut.require([]) +def method_without_expecteds(opts): + return 0 + + +@sut.require(['this', 'that']) +def method_with_expecteds(opts): + return 0 + + +@sut.require([]) +def method_exception_from_inside(opts): + raise Exception('here is one') + + +class RequireDecoratorTest(unittest.TestCase): + + def test_method_without_expecteds(self): + self.assertEqual(method_without_expecteds(dict()), 0) + self.assertEqual(method_without_expecteds({}), 0) + self.assertEqual(method_without_expecteds({'this': 2}), 0) + self.assertEqual(method_without_expecteds({'that': 3}), 0) + + def test_method_with_expecteds(self): + self.assertRaises(KeyError, method_with_expecteds, dict()) + self.assertRaises(KeyError, method_with_expecteds, {}) + self.assertRaises(KeyError, method_with_expecteds, {'this': 2}) + self.assertRaises(KeyError, method_with_expecteds, {'that': 3}) + self.assertEqual(method_with_expecteds({'this': 0, 'that': 3}), 0) + + def test_method_exception_not_caught(self): + self.assertRaises(Exception, method_exception_from_inside, dict()) diff --git a/tools/scan-build-py/tests/unit/test_shell.py b/tools/scan-build-py/tests/unit/test_shell.py new file mode 100644 index 000000000000..a2904b07f5bc --- /dev/null +++ b/tools/scan-build-py/tests/unit/test_shell.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. + +import libscanbuild.shell as sut +import unittest + + +class ShellTest(unittest.TestCase): + + def test_encode_decode_are_same(self): + def test(value): + self.assertEqual(sut.encode(sut.decode(value)), value) + + test("") + test("clang") + test("clang this and that") + + def test_decode_encode_are_same(self): + def test(value): + self.assertEqual(sut.decode(sut.encode(value)), value) + + test([]) + test(['clang']) + test(['clang', 'this', 'and', 'that']) + test(['clang', 'this and', 'that']) + test(['clang', "it's me", 'again']) + test(['clang', 'some "words" are', 'quoted']) + + def test_encode(self): + self.assertEqual(sut.encode(['clang', "it's me", 'again']), + 'clang "it\'s me" again') + self.assertEqual(sut.encode(['clang', "it(s me", 'again)']), + 'clang "it(s me" "again)"') + self.assertEqual(sut.encode(['clang', 'redirect > it']), + 'clang "redirect > it"') + self.assertEqual(sut.encode(['clang', '-DKEY="VALUE"']), + 'clang -DKEY=\\"VALUE\\"') + self.assertEqual(sut.encode(['clang', '-DKEY="value with spaces"']), + 'clang -DKEY=\\"value with spaces\\"') diff --git a/unittests/AST/SourceLocationTest.cpp b/unittests/AST/SourceLocationTest.cpp index 4c77def61bc4..9fae8d862aef 100644 --- a/unittests/AST/SourceLocationTest.cpp +++ b/unittests/AST/SourceLocationTest.cpp @@ -542,5 +542,43 @@ TEST(ObjCMessageExpr, CXXConstructExprRange) { cxxConstructExpr(), Lang_OBJCXX)); } +TEST(FunctionDecl, FunctionDeclWithThrowSpecification) { + RangeVerifier<FunctionDecl> Verifier; + Verifier.expectRange(1, 1, 1, 16); + EXPECT_TRUE(Verifier.match( + "void f() throw();\n", + functionDecl())); +} + +TEST(FunctionDecl, FunctionDeclWithNoExceptSpecification) { + RangeVerifier<FunctionDecl> Verifier; + Verifier.expectRange(1, 1, 1, 24); + EXPECT_TRUE(Verifier.match( + "void f() noexcept(false);\n", + functionDecl(), + Language::Lang_CXX11)); +} + +TEST(CXXMethodDecl, CXXMethodDeclWithThrowSpecification) { + RangeVerifier<FunctionDecl> Verifier; + Verifier.expectRange(2, 1, 2, 16); + EXPECT_TRUE(Verifier.match( + "class A {\n" + "void f() throw();\n" + "};\n", + functionDecl())); +} + +TEST(CXXMethodDecl, CXXMethodDeclWithNoExceptSpecification) { + RangeVerifier<FunctionDecl> Verifier; + Verifier.expectRange(2, 1, 2, 24); + EXPECT_TRUE(Verifier.match( + "class A {\n" + "void f() noexcept(false);\n" + "};\n", + functionDecl(), + Language::Lang_CXX11)); +} + } // end namespace ast_matchers } // end namespace clang diff --git a/unittests/Basic/VirtualFileSystemTest.cpp b/unittests/Basic/VirtualFileSystemTest.cpp index ac07035d3e1f..7abc549292e2 100644 --- a/unittests/Basic/VirtualFileSystemTest.cpp +++ b/unittests/Basic/VirtualFileSystemTest.cpp @@ -370,14 +370,6 @@ TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) { EXPECT_EQ(1, Counts[3]); // d } -template <typename T, size_t N> -std::vector<StringRef> makeStringRefVector(const T (&Arr)[N]) { - std::vector<StringRef> Vec; - for (size_t i = 0; i != N; ++i) - Vec.push_back(Arr[i]); - return Vec; -} - template <typename DirIter> static void checkContents(DirIter I, ArrayRef<StringRef> Expected) { std::error_code EC; @@ -405,20 +397,14 @@ TEST(VirtualFileSystemTest, OverlayIteration) { checkContents(O->dir_begin("/", EC), ArrayRef<StringRef>("/file1")); Upper->addRegularFile("/file2"); - { - const char *Contents[] = {"/file2", "/file1"}; - checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents)); - } + checkContents(O->dir_begin("/", EC), {"/file2", "/file1"}); Lower->addDirectory("/dir1"); Lower->addRegularFile("/dir1/foo"); Upper->addDirectory("/dir2"); Upper->addRegularFile("/dir2/foo"); checkContents(O->dir_begin("/dir2", EC), ArrayRef<StringRef>("/dir2/foo")); - { - const char *Contents[] = {"/dir2", "/file2", "/dir1", "/file1"}; - checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents)); - } + checkContents(O->dir_begin("/", EC), {"/dir2", "/file2", "/dir1", "/file1"}); } TEST(VirtualFileSystemTest, OverlayRecursiveIteration) { @@ -440,11 +426,8 @@ TEST(VirtualFileSystemTest, OverlayRecursiveIteration) { Upper->addDirectory("/dir"); Upper->addRegularFile("/dir/file2"); - { - const char *Contents[] = {"/dir", "/dir/file2", "/file1"}; - checkContents(vfs::recursive_directory_iterator(*O, "/", EC), - makeStringRefVector(Contents)); - } + checkContents(vfs::recursive_directory_iterator(*O, "/", EC), + {"/dir", "/dir/file2", "/file1"}); Lower->addDirectory("/dir1"); Lower->addRegularFile("/dir1/foo"); @@ -460,13 +443,10 @@ TEST(VirtualFileSystemTest, OverlayRecursiveIteration) { Upper->addRegularFile("/hiddenByUp"); checkContents(vfs::recursive_directory_iterator(*O, "/dir2", EC), ArrayRef<StringRef>("/dir2/foo")); - { - const char *Contents[] = { "/dir", "/dir/file2", "/dir2", "/dir2/foo", - "/hiddenByUp", "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a", - "/dir1/a/b", "/dir1/foo", "/file1" }; - checkContents(vfs::recursive_directory_iterator(*O, "/", EC), - makeStringRefVector(Contents)); - } + checkContents(vfs::recursive_directory_iterator(*O, "/", EC), + {"/dir", "/dir/file2", "/dir2", "/dir2/foo", "/hiddenByUp", + "/a", "/a/b", "/a/b/c", "/a/b/c/d", "/dir1", "/dir1/a", + "/dir1/a/b", "/dir1/foo", "/file1"}); } TEST(VirtualFileSystemTest, ThreeLevelIteration) { @@ -486,10 +466,7 @@ TEST(VirtualFileSystemTest, ThreeLevelIteration) { Lower->addRegularFile("/file1"); Upper->addRegularFile("/file3"); - { - const char *Contents[] = {"/file3", "/file2", "/file1"}; - checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents)); - } + checkContents(O->dir_begin("/", EC), {"/file3", "/file2", "/file1"}); } TEST(VirtualFileSystemTest, HiddenInIteration) { @@ -510,11 +487,9 @@ TEST(VirtualFileSystemTest, HiddenInIteration) { Middle->addRegularFile("/hiddenByUp", sys::fs::owner_write); Upper->addRegularFile("/onlyInUp", sys::fs::owner_all); Upper->addRegularFile("/hiddenByUp", sys::fs::owner_all); - { - const char *Contents[] = {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", - "/onlyInMid", "/onlyInLow"}; - checkContents(O->dir_begin("/", EC), makeStringRefVector(Contents)); - } + checkContents( + O->dir_begin("/", EC), + {"/hiddenByUp", "/onlyInUp", "/hiddenByMid", "/onlyInMid", "/onlyInLow"}); // Make sure we get the top-most entry { @@ -657,6 +632,18 @@ TEST_F(InMemoryFileSystemTest, WorkingDirectory) { Stat = FS.status("c"); ASSERT_FALSE(Stat.getError()) << Stat.getError() << "\n" << FS.toString(); + + auto ReplaceBackslashes = [](std::string S) { + std::replace(S.begin(), S.end(), '\\', '/'); + return S; + }; + NormalizedFS.setCurrentWorkingDirectory("/b/c"); + NormalizedFS.setCurrentWorkingDirectory("."); + ASSERT_EQ("/b/c", ReplaceBackslashes( + NormalizedFS.getCurrentWorkingDirectory().get())); + NormalizedFS.setCurrentWorkingDirectory(".."); + ASSERT_EQ("/b", ReplaceBackslashes( + NormalizedFS.getCurrentWorkingDirectory().get())); } // NOTE: in the tests below, we use '//root/' as our root directory, since it is @@ -1067,15 +1054,9 @@ TEST_F(VFSFromYAMLTest, DirectoryIteration) { O->pushOverlay(FS); std::error_code EC; - { - const char *Contents[] = {"//root/file1", "//root/file2", "//root/file3", - "//root/foo"}; - checkContents(O->dir_begin("//root/", EC), makeStringRefVector(Contents)); - } + checkContents(O->dir_begin("//root/", EC), + {"//root/file1", "//root/file2", "//root/file3", "//root/foo"}); - { - const char *Contents[] = {"//root/foo/bar/a", "//root/foo/bar/b"}; - checkContents(O->dir_begin("//root/foo/bar", EC), - makeStringRefVector(Contents)); - } + checkContents(O->dir_begin("//root/foo/bar", EC), + {"//root/foo/bar/a", "//root/foo/bar/b"}); } diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index c940bc75c80e..30fd6030dcc8 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -1022,6 +1022,15 @@ TEST_F(FormatTest, UnderstandsSingleLineComments) { " lineWith(); // comment\n" " // at start\n" "}")); + EXPECT_EQ("int xy; // a\n" + "int z; // b", + format("int xy; // a\n" + "int z; //b")); + EXPECT_EQ("int xy; // a\n" + "int z; // bb", + format("int xy; // a\n" + "int z; //bb", + getLLVMStyleWithColumns(12))); verifyFormat("#define A \\\n" " int i; /* iiiiiiiiiiiiiiiiiiiii */ \\\n" @@ -3607,6 +3616,7 @@ TEST_F(FormatTest, ConstructorInitializers) { FormatStyle OnePerLine = getLLVMStyle(); OnePerLine.ConstructorInitializerAllOnOneLineOrOnePerLine = true; + OnePerLine.AllowAllParametersOfDeclarationOnNextLine = false; verifyFormat("SomeClass::Constructor()\n" " : aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n" " aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n" @@ -3633,6 +3643,13 @@ TEST_F(FormatTest, ConstructorInitializers) { " : aaaaa(aaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaa,\n" " aaaaaaaaaaaaaaaaaaaaaa) {}", OnePerLine); + OnePerLine.BinPackParameters = false; + verifyFormat( + "Constructor()\n" + " : aaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaa().aaa(),\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}", + OnePerLine); OnePerLine.ColumnLimit = 60; verifyFormat("Constructor()\n" " : aaaaaaaaaaaaaaaaaaaa(a),\n" @@ -4054,6 +4071,23 @@ TEST_F(FormatTest, FormatsDeclarationsOnePerLine) { " int aaaaaaaaaaaaaaaaaaaa,\n" " int aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) {}", NoBinPacking); + + NoBinPacking.AllowAllParametersOfDeclarationOnNextLine = false; + verifyFormat("void aaaaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" + " vector<int> bbbbbbbbbbbbbbb);", + NoBinPacking); + // FIXME: This behavior difference is probably not wanted. However, currently + // we cannot distinguish BreakBeforeParameter being set because of the wrapped + // template arguments from BreakBeforeParameter being set because of the + // one-per-line formatting. + verifyFormat( + "void fffffffffff(aaaaaaaaaaaaaaaaaaaaaaaaaaa<aaaaaaaaaaaaaaaaaaaaaaa,\n" + " aaaaaaaaaa> aaaaaaaaaa);", + NoBinPacking); + verifyFormat( + "void fffffffffff(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaa<aaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaa>\n" + " aaaaaaaaaa);"); } TEST_F(FormatTest, FormatsOneParameterPerLineIfNecessary) { @@ -5450,6 +5484,7 @@ TEST_F(FormatTest, UnderstandsOverloadedOperators) { verifyFormat("bool operator!=();"); verifyFormat("int operator+();"); verifyFormat("int operator++();"); + verifyFormat("bool operator,();"); verifyFormat("bool operator();"); verifyFormat("bool operator()();"); verifyFormat("bool operator[]();"); @@ -5465,6 +5500,8 @@ TEST_F(FormatTest, UnderstandsOverloadedOperators) { verifyFormat("void operator delete[](void *ptr);"); verifyFormat("template <typename AAAAAAA, typename BBBBBBB>\n" "AAAAAAA operator/(const AAAAAAA &a, BBBBBBB &b);"); + verifyFormat("aaaaaaaaaaaaaaaaaaaaaa operator,(\n" + " aaaaaaaaaaaaaaaaaaaaa &aaaaaaaaaaaaaaaaaaaaaaaaaa) const;"); verifyFormat( "ostream &operator<<(ostream &OutputStream,\n" @@ -7726,7 +7763,12 @@ TEST_F(FormatTest, ObjCArrayLiterals) { " aaaa == bbbbbbbbbbb ? @\"aaaaaaaaaaaa\" : @\"aaaaaaaaaaaaaa\",\n" " @\"aaaaaaaaaaaaaaaaa\",\n" " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\"\n" + " @\"aaaaaaaaaaaaaaaaa\",\n" + "];"); + verifyFormat( + "NSArray *some_variable = @[\n" + " aaaa == bbbbbbbbbbb ? @\"aaaaaaaaaaaa\" : @\"aaaaaaaaaaaaaa\",\n" + " @\"aaaaaaaaaaaaaaaa\", @\"aaaaaaaaaaaaaaaa\", @\"aaaaaaaaaaaaaaaa\"\n" "];"); verifyFormat("NSArray *some_variable = @[\n" " @\"aaaaaaaaaaaaaaaaa\",\n" @@ -7734,12 +7776,6 @@ TEST_F(FormatTest, ObjCArrayLiterals) { " @\"aaaaaaaaaaaaaaaaa\",\n" " @\"aaaaaaaaaaaaaaaaa\",\n" "];"); - verifyGoogleFormat("NSArray *some_variable = @[\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\",\n" - " @\"aaaaaaaaaaaaaaaaa\"\n" - "];"); verifyFormat("NSArray *array = @[\n" " @\"a\",\n" " @\"a\",\n" // Trailing comma -> one per line. @@ -10630,6 +10666,7 @@ TEST_F(FormatTest, FormatsLambdas) { // Lambdas created through weird macros. verifyFormat("void f() {\n" " MACRO((const AA &a) { return 1; });\n" + " MACRO((AA &a) { return 1; });\n" "}"); verifyFormat("if (blah_blah(whatever, whatever, [] {\n" diff --git a/unittests/Format/FormatTestJS.cpp b/unittests/Format/FormatTestJS.cpp index 2f67bdf549ca..0e0f64a31ac6 100644 --- a/unittests/Format/FormatTestJS.cpp +++ b/unittests/Format/FormatTestJS.cpp @@ -284,13 +284,16 @@ TEST_F(FormatTestJS, ArrayLiterals) { verifyFormat("var aaaaa: List<SomeThing> =\n" " [new SomeThingAAAAAAAAAAAA(), new SomeThingBBBBBBBBB()];"); verifyFormat("return [\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" - " bbbbbbbbbbbbbbbbbbbbbbbbbbb,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbb,\n" " ccccccccccccccccccccccccccc\n" "];"); + verifyFormat("return [\n" + " aaaa().bbbbbbbb('A'),\n" + " aaaa().bbbbbbbb('B'),\n" + " aaaa().bbbbbbbb('C'),\n" + "];"); verifyFormat("var someVariable = SomeFunction([\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" - " bbbbbbbbbbbbbbbbbbbbbbbbbbb,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbb,\n" " ccccccccccccccccccccccccccc\n" "]);"); verifyFormat("var someVariable = SomeFunction([\n" @@ -298,16 +301,14 @@ TEST_F(FormatTestJS, ArrayLiterals) { "]);", getGoogleJSStyleWithColumns(51)); verifyFormat("var someVariable = SomeFunction(aaaa, [\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" - " bbbbbbbbbbbbbbbbbbbbbbbbbbb,\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbb,\n" " ccccccccccccccccccccccccccc\n" "]);"); verifyFormat("var someVariable = SomeFunction(\n" " aaaa,\n" " [\n" - " aaaaaaaaaaaaaaaaaaaaaaaaaaa,\n" - " bbbbbbbbbbbbbbbbbbbbbbbbbbb,\n" - " ccccccccccccccccccccccccccc\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbb,\n" + " cccccccccccccccccccccccccc\n" " ],\n" " aaaa);"); verifyFormat("var aaaa = aaaaa || // wrap\n" @@ -525,6 +526,12 @@ TEST_F(FormatTestJS, MultipleFunctionLiterals) { verifyFormat("getSomeLongPromise()\n" " .then(function(value) { body(); })\n" " .thenCatch(function(error) { body(); });"); + + verifyFormat("return [aaaaaaaaaaaaaaaaaaaaaa]\n" + " .aaaaaaa(function() {\n" + " //\n" + " })\n" + " .bbbbbb();"); } TEST_F(FormatTestJS, ArrowFunctions) { @@ -726,15 +733,22 @@ TEST_F(FormatTestJS, RegexLiteralExamples) { TEST_F(FormatTestJS, TypeAnnotations) { verifyFormat("var x: string;"); + verifyFormat("var x: {a: string; b: number;} = {};"); verifyFormat("function x(): string {\n return 'x';\n}"); verifyFormat("function x(): {x: string} {\n return {x: 'x'};\n}"); verifyFormat("function x(y: string): string {\n return 'x';\n}"); verifyFormat("for (var y: string in x) {\n x();\n}"); + verifyFormat("function x(y: {a?: number;} = {}): number {\n" + " return 12;\n" + "}"); verifyFormat("((a: string, b: number): string => a + b);"); verifyFormat("var x: (y: number) => string;"); verifyFormat("var x: P<string, (a: number) => string>;"); verifyFormat("var x = {y: function(): z { return 1; }};"); verifyFormat("var x = {y: function(): {a: number} { return 1; }};"); + verifyFormat("function someFunc(args: string[]):\n" + " {longReturnValue: string[]} {}", + getGoogleJSStyleWithColumns(60)); } TEST_F(FormatTestJS, ClassDeclarations) { @@ -750,6 +764,16 @@ TEST_F(FormatTestJS, ClassDeclarations) { " aaaaaaaaaaaaaaaa(aaaaaaaaaaaaaaa: aaaaaaaaaaaaaaaaaaaa):\n" " aaaaaaaaaaaaaaaaaaaaaa {}\n" "}"); + verifyFormat("foo = class Name {\n" + " constructor() {}\n" + "};"); + verifyFormat("foo = class {\n" + " constructor() {}\n" + "};"); + verifyFormat("class C {\n" + " x: {y: Z;} = {};\n" + " private y: {y: Z;} = {};\n" + "}"); // ':' is not a type declaration here. verifyFormat("class X {\n" @@ -812,6 +836,7 @@ TEST_F(FormatTestJS, MetadataAnnotations) { TEST_F(FormatTestJS, Modules) { verifyFormat("import SomeThing from 'some/module.js';"); verifyFormat("import {X, Y} from 'some/module.js';"); + verifyFormat("import a, {X, Y} from 'some/module.js';"); verifyFormat("import {\n" " VeryLongImportsAreAnnoying,\n" " VeryLongImportsAreAnnoying,\n" @@ -849,6 +874,7 @@ TEST_F(FormatTestJS, Modules) { " y: string;\n" "}"); verifyFormat("export class X { y: number; }"); + verifyFormat("export abstract class X { y: number; }"); verifyFormat("export default class X { y: number }"); verifyFormat("export default function() {\n return 1;\n}"); verifyFormat("export var x = 12;"); @@ -871,6 +897,10 @@ TEST_F(FormatTestJS, Modules) { "];"); verifyFormat("export default [];"); verifyFormat("export default () => {};"); + verifyFormat("export interface Foo { foo: number; }\n" + "export class Bar {\n" + " blah(): string { return this.blah; };\n" + "}"); } TEST_F(FormatTestJS, TemplateStrings) { @@ -933,6 +963,9 @@ TEST_F(FormatTestJS, TemplateStrings) { "var y;"); verifyFormat("var x = `\"`; // comment with matching quote \"\n" "var y;"); + EXPECT_EQ("it(`'aaaaaaaaaaaaaaa `, aaaaaaaaa);", + format("it(`'aaaaaaaaaaaaaaa `, aaaaaaaaa) ;", + getGoogleJSStyleWithColumns(40))); // Backticks in a comment - not a template string. EXPECT_EQ("var x = 1 // `/*a`;\n" " ;", @@ -1018,5 +1051,15 @@ TEST_F(FormatTestJS, WrapAfterParen) { " bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) {\n}"); } +TEST_F(FormatTestJS, JSDocAnnotations) { + EXPECT_EQ("/**\n" + " * @export {this.is.a.long.path.to.a.Type}\n" + " */", + format("/**\n" + " * @export {this.is.a.long.path.to.a.Type}\n" + " */", + getGoogleJSStyleWithColumns(20))); +} + } // end namespace tooling } // end namespace clang diff --git a/unittests/Format/FormatTestSelective.cpp b/unittests/Format/FormatTestSelective.cpp index d53d1c042921..699600c42d9f 100644 --- a/unittests/Format/FormatTestSelective.cpp +++ b/unittests/Format/FormatTestSelective.cpp @@ -162,6 +162,13 @@ TEST_F(FormatTestSelective, FormatsCommentsLocally) { "// This is\n" "// not formatted. ", 0, 0)); + EXPECT_EQ("int x; // Format this line.\n" + "int xx; //\n" + "int xxxxx; //", + format("int x; // Format this line.\n" + "int xx; //\n" + "int xxxxx; //", + 0, 0)); } TEST_F(FormatTestSelective, IndividualStatementsOfNestedBlocks) { |