diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2012-08-19 10:33:04 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2012-08-19 10:33:04 +0000 |
commit | 657bc3d9848e3be92029b2416031340988cd0111 (patch) | |
tree | 5b9c2fa9d79942fbdce3d618e37e27c18263af9a | |
parent | 56d91b49b13fe55c918afbda19f6165b5fbff87a (diff) | |
download | src-657bc3d9848e3be92029b2416031340988cd0111.tar.gz src-657bc3d9848e3be92029b2416031340988cd0111.zip |
Vendor import of clang trunk r162107:vendor/clang/clang-trunk-r162107
Notes
Notes:
svn path=/vendor/clang/dist/; revision=239392
svn path=/vendor/clang/clang-trunk-r162107/; revision=239393; tag=vendor/clang/clang-trunk-r162107
157 files changed, 4423 insertions, 1127 deletions
diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index eac3c69997be..40477b82f5f2 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -142,6 +142,13 @@ <li><a href="#ts_slr"><tt>shared_locks_required(...)</tt></a></li> </ul> </li> +<li><a href="#type_safety">Type Safety Checking</a> + <ul> + <li><a href="#argument_with_type_tag"><tt>argument_with_type_tag(...)</tt></a></li> + <li><a href="#pointer_with_type_tag"><tt>pointer_with_type_tag(...)</tt></a></li> + <li><a href="#type_tag_for_datatype"><tt>type_tag_for_datatype(...)</tt></a></li> + </ul> +</li> </ul> <!-- ======================================================================= --> @@ -1913,6 +1920,161 @@ declaration to specify that the function must be called while holding the listed shared locks. Arguments must be lockable type, and there must be at least one argument.</p> +<!-- ======================================================================= --> +<h2 id="type_safety">Type Safety Checking</h2> +<!-- ======================================================================= --> + +<p>Clang supports additional attributes to enable checking type safety +properties that can't be enforced by C type system. Usecases include:</p> +<ul> +<li>MPI library implementations, where these attributes enable checking that + buffer type matches the passed <tt>MPI_Datatype</tt>;</li> +<li>for HDF5 library there is a similar usecase as MPI;</li> +<li>checking types of variadic functions' arguments for functions like + <tt>fcntl()</tt> and <tt>ioctl()</tt>.</li> +</ul> + +<p>You can detect support for these attributes with __has_attribute(). For +example:</p> + +<blockquote> +<pre> +#if defined(__has_attribute) +# if __has_attribute(argument_with_type_tag) && \ + __has_attribute(pointer_with_type_tag) && \ + __has_attribute(type_tag_for_datatype) +# define ATTR_MPI_PWT(buffer_idx, type_idx) __attribute__((pointer_with_type_tag(mpi,buffer_idx,type_idx))) +/* ... other macros ... */ +# endif +#endif + +#if !defined(ATTR_MPI_PWT) +#define ATTR_MPI_PWT(buffer_idx, type_idx) +#endif + +int MPI_Send(void *buf, int count, MPI_Datatype datatype /*, other args omitted */) + ATTR_MPI_PWT(1,3); +</pre> +</blockquote> + +<h3 id="argument_with_type_tag"><tt>argument_with_type_tag(...)</tt></h3> + +<p>Use <tt>__attribute__((argument_with_type_tag(arg_kind, arg_idx, +type_tag_idx)))</tt> on a function declaration to specify that the function +accepts a type tag that determines the type of some other argument. +<tt>arg_kind</tt> is an identifier that should be used when annotating all +applicable type tags.</p> + +<p>This attribute is primarily useful for checking arguments of variadic +functions (<tt>pointer_with_type_tag</tt> can be used in most of non-variadic +cases).</p> + +<p>For example:</p> +<blockquote> +<pre> +int fcntl(int fd, int cmd, ...) + __attribute__(( argument_with_type_tag(fcntl,3,2) )); +</pre> +</blockquote> + +<h3 id="pointer_with_type_tag"><tt>pointer_with_type_tag(...)</tt></h3> + +<p>Use <tt>__attribute__((pointer_with_type_tag(ptr_kind, ptr_idx, +type_tag_idx)))</tt> on a function declaration to specify that the +function a type tag that determines the pointee type of some other pointer +argument.</p> + +<p>For example:</p> +<blockquote> +<pre> +int MPI_Send(void *buf, int count, MPI_Datatype datatype /*, other args omitted */) + __attribute__(( pointer_with_type_tag(mpi,1,3) )); +</pre> +</blockquote> + +<h3 id="type_tag_for_datatype"><tt>type_tag_for_datatype(...)</tt></h3> + +<p>Clang supports annotating type tags of two forms.</p> + +<ul> +<li><b>Type tag that is an expression containing a reference to some declared +identifier.</b> Use <tt>__attribute__((type_tag_for_datatype(kind, type)))</tt> +on a declaration with that identifier: + +<blockquote> +<pre> +extern struct mpi_datatype mpi_datatype_int + __attribute__(( type_tag_for_datatype(mpi,int) )); +#define MPI_INT ((MPI_Datatype) &mpi_datatype_int) +</pre> +</blockquote></li> + +<li><b>Type tag that is an integral literal.</b> Introduce a <tt>static +const</tt> variable with a corresponding initializer value and attach +<tt>__attribute__((type_tag_for_datatype(kind, type)))</tt> on that +declaration, for example: + +<blockquote> +<pre> +#define MPI_INT ((MPI_Datatype) 42) +static const MPI_Datatype mpi_datatype_int + __attribute__(( type_tag_for_datatype(mpi,int) )) = 42 +</pre> +</blockquote></li> +</ul> + +<p>The attribute also accepts an optional third argument that determines how +the expression is compared to the type tag. There are two supported flags:</p> + +<ul><li><tt>layout_compatible</tt> will cause types to be compared according to +layout-compatibility rules (C++11 [class.mem] p 17, 18). This is +implemented to support annotating types like <tt>MPI_DOUBLE_INT</tt>. + +<p>For example:</p> +<blockquote> +<pre> +/* In mpi.h */ +struct internal_mpi_double_int { double d; int i; }; +extern struct mpi_datatype mpi_datatype_double_int + __attribute__(( type_tag_for_datatype(mpi, struct internal_mpi_double_int, + layout_compatible) )); + +#define MPI_DOUBLE_INT ((MPI_Datatype) &mpi_datatype_double_int) + +/* In user code */ +struct my_pair { double a; int b; }; +struct my_pair *buffer; +MPI_Send(buffer, 1, MPI_DOUBLE_INT /*, ... */); // no warning + +struct my_int_pair { int a; int b; } +struct my_int_pair *buffer2; +MPI_Send(buffer2, 1, MPI_DOUBLE_INT /*, ... */); // warning: actual buffer element + // type 'struct my_int_pair' + // doesn't match specified MPI_Datatype +</pre> +</blockquote> +</li> + +<li><tt>must_be_null</tt> specifies that the expression should be a null +pointer constant, for example: + +<blockquote> +<pre> +/* In mpi.h */ +extern struct mpi_datatype mpi_datatype_null + __attribute__(( type_tag_for_datatype(mpi, void, must_be_null) )); + +#define MPI_DATATYPE_NULL ((MPI_Datatype) &mpi_datatype_null) + +/* In user code */ +MPI_Send(buffer, 1, MPI_DATATYPE_NULL /*, ... */); // warning: MPI_DATATYPE_NULL + // was specified but buffer + // is not a null pointer +</pre> +</blockquote> +</li> +</ul> + </div> </body> </html> diff --git a/docs/ReleaseNotes.html b/docs/ReleaseNotes.html index 2108909c9729..7a6a508bb54d 100644 --- a/docs/ReleaseNotes.html +++ b/docs/ReleaseNotes.html @@ -186,6 +186,25 @@ supported by the target, or if the compiler determines that a more specific model can be used. </p> +<h4>Type safety attributes</h4> +<p>Clang now supports type safety attributes that allow checking during compile +time that 'void *' function arguments and arguments for variadic functions are +of a particular type which is determined by some other argument to the same +function call.</p> + +<p>Usecases include:</p> +<ul> +<li>MPI library implementations, where these attributes enable checking that + buffer type matches the passed <code>MPI_Datatype</code>;</li> +<li> HDF5 library -- similar usecase as for MPI;</li> +<li> checking types of variadic functions' arguments for functions like +<code>fcntl()</code> and <code>ioctl()</code>.</li> +</ul> + +<p>See entries for <code>argument_with_type_tag</code>, +<code>pointer_with_type_tag</code> and <code>type_tag_for_datatype</code> +attributes in Clang language extensions documentation.</p> + <!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = --> <h3 id="newflags">New Compiler Flags</h3> <!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = --> diff --git a/docs/analyzer/IPA.txt b/docs/analyzer/IPA.txt new file mode 100644 index 000000000000..569197712894 --- /dev/null +++ b/docs/analyzer/IPA.txt @@ -0,0 +1,96 @@ +Inlining +======== + +Inlining Modes +----------------------- +-analyzer-ipa=none - All inlining is disabled. +-analyzer-ipa=inlining - Turns on inlining when we can confidently find the function/method body corresponding to the call. (C functions, static functions, devirtualized C++ methods, ObjC class methods, ObjC instance methods when we are confident about the dynamic type of the instance). +-analyzer-ipa=dynamic - Inline instance methods for which the type is determined at runtime and we are not 100% sure that our type info is correct. For virtual calls, inline the most plausible definition. +-analyzer-ipa=dynamic-bifurcate - Same as -analyzer-ipa=dynamic, but the path is split. We inline on one branch and do not inline on the other. This mode does not drop the coverage in cases when the parent class has code that is only exercised when some of its methods are overriden. + +Currently, -analyzer-ipa=inlining is the default mode. + +Basics of Implementation +----------------------- + +The low-level mechanism of inlining a function is handled in ExprEngine::inlineCall and ExprEngine::processCallExit. If the conditions are right for inlining, a CallEnter node is created and added to the analysis work list. The CallEnter node marks the change to a new LocationContext representing the called function, and its state includes the contents of the new stack frame. When the CallEnter node is actually processed, its single successor will be a edge to the first CFG block in the function. + +Exiting an inlined function is a bit more work, fortunately broken up into reasonable steps: +1. The CoreEngine realizes we're at the end of an inlined call and generates a CallExitBegin node. +2. ExprEngine takes over (in processCallExit) and finds the return value of the function, if it has one. This is bound to the expression that triggered the call. (In the case of calls without origin expressions, such as destructors, this step is skipped.) +3. Dead symbols and bindings are cleaned out from the state, including any local bindings. +4. A CallExitEnd node is generated, which marks the transition back to the caller's LocationContext. +5. Custom post-call checks are processed and the final nodes are pushed back onto the work list, so that evaluation of the caller can continue. + +Retry Without Inlining +----------------------- + +In some cases, we would like to retry analyzes without inlining the particular call. Currently, we use this technique to recover the coverage in case we stop analyzing a path due to exceeding the maximum block count inside an inlined function. When this situation is detected, we walk up the path to find the first node before inlining was started and enqueue it on the WorkList with a special ReplayWithoutInlining bit added to it (ExprEngine::replayWithoutInlining). + +Deciding when to inline +----------------------- +In general, we try to inline as much as possible, since it provides a better summary of what actually happens in the program. However, there are some cases where we choose not to inline: +- if there is no definition available (of course) +- if we can't create a CFG or compute variable liveness for the function +- if we reach a cutoff of maximum stack depth (to avoid infinite recursion) +- if the function is variadic +- in C++, we don't inline constructors unless we know the destructor will be inlined as well +- in C++, we don't inline allocators (custom operator new implementations), since we don't properly handle deallocators (at the time of this writing) +- "Dynamic" calls are handled specially; see below. +- Engine:FunctionSummaries map stores additional information about declarations, some of which is collected at runtime based on previous analyzes of the function. We do not inline functions which were not profitable to inline in a different context (for example, if the maximum block count was exceeded, see Retry Without Inlining). + + +Dynamic calls and devirtualization +---------------------------------- +"Dynamic" calls are those that are resolved at runtime, such as C++ virtual method calls and Objective-C message sends. Due to the path-sensitive nature of the analyzer, we may be able to figure out the dynamic type of the object whose method is being called and thus "devirtualize" the call, i.e. find the actual method that will be called at runtime. (Obviously this is not always possible.) This is handled by CallEvent's getRuntimeDefinition method. + +Type information is tracked as DynamicTypeInfo, stored within the program state. If no DynamicTypeInfo has been explicitly set for a region, it will be inferred from the region's type or associated symbol. Information from symbolic regions is weaker than from true typed regions; a C++ object declared "A obj" is known to have the class 'A', but a reference "A &ref" may dynamically be a subclass of 'A'. The DynamicTypePropagation checker gathers and propagates the type information. + +(Warning: not all of the existing analyzer code has been retrofitted to use DynamicTypeInfo, nor is it universally appropriate. In particular, DynamicTypeInfo always applies to a region with all casts stripped off, but sometimes the information provided by casts can be useful.) + +When asked to provide a definition, the CallEvents for dynamic calls will use the type info in their state to provide the best definition of the method to be called. In some cases this devirtualization can be perfect or near-perfect, and we can inline the definition as usual. In others we can make a guess, but report that our guess may not be the method actually called at runtime. + +The -analyzer-ipa option has four different modes: none, inlining, dynamic, and dynamic-bifurcate. Under -analyzer-ipa=dynamic, all dynamic calls are inlined, whether we are certain or not that this will actually be the definition used at runtime. Under -analyzer-ipa=inlining, only "near-perfect" devirtualized calls are inlined*, and other dynamic calls are evaluated conservatively (as if no definition were available). + +* Currently, no Objective-C messages are not inlined under -analyzer-ipa=inlining, even if we are reasonably confident of the type of the receiver. We plan to enable this once we have tested our heuristics more thoroughly. + +The last option, -analyzer-ipa=dynamic-bifurcate, behaves similarly to "dynamic", but performs a conservative invalidation in the general virtual case in /addition/ to inlining. The details of this are discussed below. + + +Bifurcation +----------- +ExprEngine::BifurcateCall implements the -analyzer-ipa=dynamic-bifurcate mode. When a call is made on a region with dynamic type information, we bifurcate the path and add the region's processing mode to the GDM. Currently, there are 2 modes: DynamicDispatchModeInlined and DynamicDispatchModeConservative. Going forward, we consult the state of the region to make decisions on whether the calls should be inlined or not, which ensures that we have at most one split per region. The modes model the cases when the dynamic type information is perfectly correct and when the info is not correct (i.e. where the region is a subclass of the type we store in DynamicTypeInfo). + +Bifurcation mode allows for increased coverage in cases where the parent method contains code which is only executed when the class is subclassed. The disadvantages of this mode are a (considerable?) performance hit and the possibility of false positives on the path where the conservative mode is used. + + +Objective-C Message Heuristics +------------------------------ +We rely on a set of heuristics to partition the set of ObjC method calls into ones that require bifurcation and ones that do not (can or cannot be a subclass). Below are the cases when we consider that the dynamic type of the object is precise (cannot be a subclass): + - If the object was created with +alloc or +new and initialized with an -init method. + - If the calls are property accesses using dot syntax. This is based on the assumption that children rarely override properties, or do so in an essentially compatible way. + - If the class interface is declared inside the main source file. In this case it is unlikely that it will be subclassed. + - If the method is not declared outside of main source file, either by the receiver's class or by any superclasses. + + +C++ Inlining Caveats +-------------------- +C++11 [class.cdtor]p4 describes how the vtable of an object is modified as it is being constructed or destructed; that is, the type of the object depends on which base constructors have been completed. This is tracked using dynamic type info in the DynamicTypePropagation checker. + +Temporaries are poorly modelled right now because we're not confident in the placement + +'new' is poorly modelled due to some nasty CFG/design issues (elaborated in PR12014). 'delete' is essentially not modelled at all. + +Arrays of objects are modeled very poorly right now. We run only the first constructor and first destructor. Because of this, we don't inline any constructors or destructors for arrays. + + +CallEvent +========= + +A CallEvent represents a specific call to a function, method, or other body of code. It is path-sensitive, containing both the current state (ProgramStateRef) and stack space (LocationContext), and provides uniform access to the argument values and return type of a call, no matter how the call is written in the source or what sort of code body is being invoked. + +(For those familiar with Cocoa, CallEvent is roughly equivalent to NSInvocation.) + +CallEvent should be used whenever there is logic dealing with function calls that does not care how the call occurred. Examples include checking that arguments satisfy preconditions (such as __attribute__((nonnull))), and attempting to inline a call. + +CallEvents are reference-counted objects managed by a CallEventManager. While there is no inherent issue with persisting them (say, in the state's GDM), they are intended for short-lived use, and can be recreated from CFGElements or StackFrameContexts fairly easily. diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 8fd7d6ef4233..cad3ad2b5f9c 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -474,8 +474,17 @@ public: Data.setPointer(RC); } + const Decl *getOriginalDecl() const LLVM_READONLY { + return OriginalDecl; + } + + void setOriginalDecl(const Decl *Orig) { + OriginalDecl = Orig; + } + private: llvm::PointerIntPair<const RawComment *, 2, Kind> Data; + const Decl *OriginalDecl; }; /// \brief Mapping from declarations to comments attached to any @@ -485,6 +494,10 @@ public: /// lazily. mutable llvm::DenseMap<const Decl *, RawCommentAndCacheFlags> RedeclComments; + /// \brief Mapping from declarations to parsed comments attached to any + /// redeclaration. + mutable llvm::DenseMap<const Decl *, comments::FullComment *> ParsedComments; + /// \brief Return the documentation comment attached to a given declaration, /// without looking into cache. RawComment *getRawCommentForDeclNoCache(const Decl *D) const; @@ -500,7 +513,12 @@ public: /// \brief Return the documentation comment attached to a given declaration. /// Returns NULL if no comment is attached. - const RawComment *getRawCommentForAnyRedecl(const Decl *D) const; + /// + /// \param OriginalDecl if not NULL, is set to declaration AST node that had + /// the comment, if the comment we found comes from a redeclaration. + const RawComment *getRawCommentForAnyRedecl( + const Decl *D, + const Decl **OriginalDecl = NULL) const; /// Return parsed documentation comment attached to a given declaration. /// Returns NULL if no comment is attached. diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index 27b44d4bd97b..b17bd48b7d03 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -105,7 +105,8 @@ public: virtual bool isLateParsed() const { return false; } // Pretty print this attribute. - virtual void printPretty(llvm::raw_ostream &OS, ASTContext &C) const = 0; + virtual void printPretty(llvm::raw_ostream &OS, + const PrintingPolicy &Policy) const = 0; // Implement isa/cast/dyncast/etc. static bool classof(const Attr *) { return true; } diff --git a/include/clang/AST/CommentCommandTraits.h b/include/clang/AST/CommentCommandTraits.h index f1883755e520..5f0269a4650b 100644 --- a/include/clang/AST/CommentCommandTraits.h +++ b/include/clang/AST/CommentCommandTraits.h @@ -35,14 +35,14 @@ public: /// A verbatim-like block command eats every character (except line starting /// decorations) until matching end command is seen or comment end is hit. /// - /// \param BeginName name of the command that starts the verbatim block. + /// \param StartName name of the command that starts the verbatim block. /// \param [out] EndName name of the command that ends the verbatim block. /// /// \returns true if a given command is a verbatim block command. bool isVerbatimBlockCommand(StringRef StartName, StringRef &EndName) const; /// \brief Register a new verbatim block command. - void addVerbatimBlockCommand(StringRef BeginName, StringRef EndName); + void addVerbatimBlockCommand(StringRef StartName, StringRef EndName); /// \brief Check if a given command is a verbatim line command. /// @@ -90,7 +90,7 @@ public: private: struct VerbatimBlockCommand { - StringRef BeginName; + StringRef StartName; StringRef EndName; }; diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index ac2cc9ec4d27..0f596095f74a 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -858,10 +858,10 @@ public: raw_ostream &Out, const PrintingPolicy &Policy, unsigned Indentation = 0); // Debuggers don't usually respect default arguments. - LLVM_ATTRIBUTE_USED void dump() const { dump(llvm::errs()); } + LLVM_ATTRIBUTE_USED void dump() const; void dump(raw_ostream &Out) const; // Debuggers don't usually respect default arguments. - LLVM_ATTRIBUTE_USED void dumpXML() const { dumpXML(llvm::errs()); } + LLVM_ATTRIBUTE_USED void dumpXML() const; void dumpXML(raw_ostream &OS) const; private: diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 851e3105bc0d..2d95f038dfd4 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -1646,14 +1646,17 @@ public: /// \brief Find the method in RD that corresponds to this one. /// /// Find if RD or one of the classes it inherits from override this method. - /// If so, return it. RD is assumed to be a base class of the class defining - /// this method (or be the class itself). + /// If so, return it. RD is assumed to be a subclass of the class defining + /// this method (or be the class itself), unless MayBeBase is set to true. CXXMethodDecl * - getCorrespondingMethodInClass(const CXXRecordDecl *RD); + getCorrespondingMethodInClass(const CXXRecordDecl *RD, + bool MayBeBase = false); const CXXMethodDecl * - getCorrespondingMethodInClass(const CXXRecordDecl *RD) const { - return const_cast<CXXMethodDecl*>(this)->getCorrespondingMethodInClass(RD); + getCorrespondingMethodInClass(const CXXRecordDecl *RD, + bool MayBeBase = false) const { + return const_cast<CXXMethodDecl *>(this) + ->getCorrespondingMethodInClass(RD, MayBeBase); } // Implement isa/cast/dyncast/etc. diff --git a/include/clang/AST/DeclGroup.h b/include/clang/AST/DeclGroup.h index 63cdac594920..cda6ae520af4 100644 --- a/include/clang/AST/DeclGroup.h +++ b/include/clang/AST/DeclGroup.h @@ -26,7 +26,11 @@ class DeclGroupIterator; class DeclGroup { // FIXME: Include a TypeSpecifier object. - unsigned NumDecls; + union { + unsigned NumDecls; + + Decl *Aligner; + }; private: DeclGroup() : NumDecls(0) {} diff --git a/include/clang/AST/DeclLookups.h b/include/clang/AST/DeclLookups.h index b8abe97be1fb..867b46525753 100644 --- a/include/clang/AST/DeclLookups.h +++ b/include/clang/AST/DeclLookups.h @@ -67,7 +67,7 @@ public: DeclContext::all_lookups_iterator DeclContext::lookups_begin() const { DeclContext *Primary = const_cast<DeclContext*>(this)->getPrimaryContext(); - if (hasExternalVisibleStorage()) + if (Primary->hasExternalVisibleStorage()) getParentASTContext().getExternalSource()->completeVisibleDeclsMap(Primary); if (StoredDeclsMap *Map = Primary->buildLookup()) return all_lookups_iterator(Map->begin(), Map->end()); @@ -76,7 +76,7 @@ DeclContext::all_lookups_iterator DeclContext::lookups_begin() const { DeclContext::all_lookups_iterator DeclContext::lookups_end() const { DeclContext *Primary = const_cast<DeclContext*>(this)->getPrimaryContext(); - if (hasExternalVisibleStorage()) + if (Primary->hasExternalVisibleStorage()) getParentASTContext().getExternalSource()->completeVisibleDeclsMap(Primary); if (StoredDeclsMap *Map = Primary->buildLookup()) return all_lookups_iterator(Map->end(), Map->end()); diff --git a/include/clang/AST/PrettyPrinter.h b/include/clang/AST/PrettyPrinter.h index 2e34dc8cbd5c..f2c015fffe94 100644 --- a/include/clang/AST/PrettyPrinter.h +++ b/include/clang/AST/PrettyPrinter.h @@ -34,19 +34,19 @@ public: struct PrintingPolicy { /// \brief Create a default printing policy for C. PrintingPolicy(const LangOptions &LO) - : Indentation(2), LangOpts(LO), SuppressSpecifiers(false), + : LangOpts(LO), Indentation(2), SuppressSpecifiers(false), SuppressTagKeyword(false), SuppressTag(false), SuppressScope(false), SuppressUnwrittenScope(false), SuppressInitializers(false), - Dump(false), ConstantArraySizeAsWritten(false), - AnonymousTagLocations(true), SuppressStrongLifetime(false), - Bool(LO.Bool) { } - - /// \brief The number of spaces to use to indent each line. - unsigned Indentation : 8; + ConstantArraySizeAsWritten(false), AnonymousTagLocations(true), + SuppressStrongLifetime(false), Bool(LO.Bool), + DumpSourceManager(0) { } /// \brief What language we're printing. LangOptions LangOpts; + /// \brief The number of spaces to use to indent each line. + unsigned Indentation : 8; + /// \brief Whether we should suppress printing of the actual specifiers for /// the given type or declaration. /// @@ -103,12 +103,6 @@ struct PrintingPolicy { /// internal initializer constructed for x will not be printed. bool SuppressInitializers : 1; - /// \brief True when we are "dumping" rather than "pretty-printing", - /// where dumping involves printing the internal details of the AST - /// and pretty-printing involves printing something similar to - /// source code. - bool Dump : 1; - /// \brief Whether we should print the sizes of constant array expressions /// as written in the sources. /// @@ -139,6 +133,12 @@ struct PrintingPolicy { /// \brief Whether we can use 'bool' rather than '_Bool', even if the language /// doesn't actually have 'bool' (because, e.g., it is defined as a macro). unsigned Bool : 1; + + /// \brief If we are "dumping" rather than "pretty-printing", this points to + /// a SourceManager which will be used to dump SourceLocations. Dumping + /// involves printing the internal details of the AST and pretty-printing + /// involves printing something similar to source code. + SourceManager *DumpSourceManager; }; } // end namespace clang diff --git a/include/clang/AST/RawCommentList.h b/include/clang/AST/RawCommentList.h index 4901d07c3cdc..630626b438ee 100644 --- a/include/clang/AST/RawCommentList.h +++ b/include/clang/AST/RawCommentList.h @@ -55,16 +55,11 @@ public: /// Is this comment attached to any declaration? bool isAttached() const LLVM_READONLY { - return !DeclOrParsedComment.isNull(); + return IsAttached; } - /// Return the declaration that this comment is attached to. - const Decl *getDecl() const; - - /// Set the declaration that this comment is attached to. - void setDecl(const Decl *D) { - assert(DeclOrParsedComment.isNull()); - DeclOrParsedComment = D; + void setAttached() { + IsAttached = true; } /// Returns true if it is a comment that should be put after a member: @@ -118,28 +113,23 @@ public: return extractBriefText(Context); } - /// Returns a \c FullComment AST node, parsing the comment if needed. - comments::FullComment *getParsed(const ASTContext &Context) const { - if (comments::FullComment *FC = - DeclOrParsedComment.dyn_cast<comments::FullComment *>()) - return FC; - - return parse(Context); - } + /// Parse the comment, assuming it is attached to decl \c D. + comments::FullComment *parse(const ASTContext &Context, const Decl *D) const; private: SourceRange Range; mutable StringRef RawText; mutable const char *BriefText; - mutable llvm::PointerUnion<const Decl *, comments::FullComment *> - DeclOrParsedComment; mutable bool RawTextValid : 1; ///< True if RawText is valid mutable bool BriefTextValid : 1; ///< True if BriefText is valid unsigned Kind : 3; + /// True if comment is attached to a declaration in ASTContext. + bool IsAttached : 1; + bool IsTrailingComment : 1; bool IsAlmostTrailingComment : 1; @@ -152,7 +142,7 @@ private: RawComment(SourceRange SR, CommentKind K, bool IsTrailingComment, bool IsAlmostTrailingComment) : Range(SR), RawTextValid(false), BriefTextValid(false), Kind(K), - IsTrailingComment(IsTrailingComment), + IsAttached(false), IsTrailingComment(IsTrailingComment), IsAlmostTrailingComment(IsAlmostTrailingComment), BeginLineValid(false), EndLineValid(false) { } @@ -161,8 +151,6 @@ private: const char *extractBriefText(const ASTContext &Context) const; - comments::FullComment *parse(const ASTContext &Context) const; - friend class ASTReader; }; diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 79e1920159cd..35fb69312b6e 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -373,15 +373,9 @@ public: /// dumpPretty/printPretty - These two methods do a "pretty print" of the AST /// back to its original source language syntax. - void dumpPretty(ASTContext& Context) const; + void dumpPretty(ASTContext &Context) const; void printPretty(raw_ostream &OS, PrinterHelper *Helper, const PrintingPolicy &Policy, - unsigned Indentation = 0) const { - printPretty(OS, *(ASTContext*)0, Helper, Policy, Indentation); - } - void printPretty(raw_ostream &OS, ASTContext &Context, - PrinterHelper *Helper, - const PrintingPolicy &Policy, unsigned Indentation = 0) const; /// viewAST - Visualize an AST rooted at this Stmt* using GraphViz. Only @@ -1620,36 +1614,40 @@ public: /// MSAsmStmt - This represents a MS inline-assembly statement extension. /// class MSAsmStmt : public Stmt { - SourceLocation AsmLoc, EndLoc; + SourceLocation AsmLoc, LBraceLoc, EndLoc; std::string AsmStr; bool IsSimple; bool IsVolatile; unsigned NumAsmToks; - unsigned NumLineEnds; + unsigned NumInputs; + unsigned NumOutputs; unsigned NumClobbers; Token *AsmToks; - unsigned *LineEnds; + IdentifierInfo **Names; Stmt **Exprs; StringRef *Clobbers; public: - MSAsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, - bool isvolatile, ArrayRef<Token> asmtoks, - ArrayRef<unsigned> lineends, StringRef asmstr, - ArrayRef<StringRef> clobbers, SourceLocation endloc); + MSAsmStmt(ASTContext &C, SourceLocation asmloc, SourceLocation lbraceloc, + bool issimple, bool isvolatile, ArrayRef<Token> asmtoks, + ArrayRef<IdentifierInfo*> inputs, ArrayRef<IdentifierInfo*> outputs, + StringRef asmstr, ArrayRef<StringRef> clobbers, + SourceLocation endloc); SourceLocation getAsmLoc() const { return AsmLoc; } void setAsmLoc(SourceLocation L) { AsmLoc = L; } + SourceLocation getLBraceLoc() const { return LBraceLoc; } + void setLBraceLoc(SourceLocation L) { LBraceLoc = L; } SourceLocation getEndLoc() const { return EndLoc; } void setEndLoc(SourceLocation L) { EndLoc = L; } + bool hasBraces() const { return LBraceLoc.isValid(); } + unsigned getNumAsmToks() { return NumAsmToks; } Token *getAsmToks() { return AsmToks; } - unsigned getNumLineEnds() { return NumLineEnds; } - unsigned *getLineEnds() { return LineEnds; } bool isVolatile() const { return IsVolatile; } void setVolatile(bool V) { IsVolatile = V; } @@ -1665,7 +1663,7 @@ public: //===--- Other ---===// unsigned getNumClobbers() const { return NumClobbers; } - StringRef getClobber(unsigned i) { return Clobbers[i]; } + StringRef getClobber(unsigned i) const { return Clobbers[i]; } SourceRange getSourceRange() const LLVM_READONLY { return SourceRange(AsmLoc, EndLoc); diff --git a/include/clang/AST/TemplateBase.h b/include/clang/AST/TemplateBase.h index 54c9f2c534d2..5047028e5eac 100644 --- a/include/clang/AST/TemplateBase.h +++ b/include/clang/AST/TemplateBase.h @@ -510,17 +510,23 @@ public: /// This is safe to be used inside an AST node, in contrast with /// TemplateArgumentListInfo. struct ASTTemplateArgumentListInfo { - /// \brief The source location of the left angle bracket ('<'); + /// \brief The source location of the left angle bracket ('<'). SourceLocation LAngleLoc; - /// \brief The source location of the right angle bracket ('>'); + /// \brief The source location of the right angle bracket ('>'). SourceLocation RAngleLoc; - /// \brief The number of template arguments in TemplateArgs. - /// The actual template arguments (if any) are stored after the - /// ExplicitTemplateArgumentList structure. - unsigned NumTemplateArgs; - + union { + /// \brief The number of template arguments in TemplateArgs. + /// The actual template arguments (if any) are stored after the + /// ExplicitTemplateArgumentList structure. + unsigned NumTemplateArgs; + + /// Force ASTTemplateArgumentListInfo to the right alignment + /// for the following array of TemplateArgumentLocs. + void *Aligner; + }; + /// \brief Retrieve the template arguments TemplateArgumentLoc *getTemplateArgs() { return reinterpret_cast<TemplateArgumentLoc *> (this + 1); diff --git a/include/clang/AST/TypeLoc.h b/include/clang/AST/TypeLoc.h index 1d1c1d172573..11a878d6b0f3 100644 --- a/include/clang/AST/TypeLoc.h +++ b/include/clang/AST/TypeLoc.h @@ -1061,7 +1061,6 @@ public: struct FunctionLocInfo { SourceLocation LocalRangeBegin; SourceLocation LocalRangeEnd; - bool TrailingReturn; }; /// \brief Wrapper for source info for functions. @@ -1084,13 +1083,6 @@ public: getLocalData()->LocalRangeEnd = L; } - bool getTrailingReturn() const { - return getLocalData()->TrailingReturn; - } - void setTrailingReturn(bool Trailing) { - getLocalData()->TrailingReturn = Trailing; - } - ArrayRef<ParmVarDecl *> getParams() const { return ArrayRef<ParmVarDecl *>(getParmArray(), getNumArgs()); } @@ -1119,7 +1111,6 @@ public: void initializeLocal(ASTContext &Context, SourceLocation Loc) { setLocalRangeBegin(Loc); setLocalRangeEnd(Loc); - setTrailingReturn(false); for (unsigned i = 0, e = getNumArgs(); i != e; ++i) setArg(i, NULL); } diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h index 37e82e8318dd..33ef3dc8d6e0 100644 --- a/include/clang/ASTMatchers/ASTMatchers.h +++ b/include/clang/ASTMatchers/ASTMatchers.h @@ -50,6 +50,7 @@ #include "clang/ASTMatchers/ASTMatchersMacros.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Regex.h" +#include <iterator> namespace clang { namespace ast_matchers { @@ -195,6 +196,75 @@ AST_MATCHER_P(ClassTemplateSpecializationDecl, hasAnyTemplateArgument, return false; } +/// \brief Matches expressions that match InnerMatcher after any implicit casts +/// are stripped off. +/// +/// Parentheses and explicit casts are not discarded. +/// Given +/// int arr[5]; +/// int a = 0; +/// char b = 0; +/// const int c = a; +/// int *d = arr; +/// long e = (long) 0l; +/// The matchers +/// variable(hasInitializer(ignoringImpCasts(integerLiteral()))) +/// variable(hasInitializer(ignoringImpCasts(declarationReference()))) +/// would match the declarations for a, b, c, and d, but not e. +/// while +/// variable(hasInitializer(integerLiteral())) +/// variable(hasInitializer(declarationReference())) +/// only match the declarations for b, c, and d. +AST_MATCHER_P(Expr, ignoringImpCasts, + internal::Matcher<Expr>, InnerMatcher) { + return InnerMatcher.matches(*Node.IgnoreImpCasts(), Finder, Builder); +} + +/// \brief Matches expressions that match InnerMatcher after parentheses and +/// casts are stripped off. +/// +/// Implicit and non-C Style casts are also discarded. +/// Given +/// int a = 0; +/// char b = (0); +/// void* c = reinterpret_cast<char*>(0); +/// char d = char(0); +/// The matcher +/// variable(hasInitializer(ignoringParenCasts(integerLiteral()))) +/// would match the declarations for a, b, c, and d. +/// while +/// variable(hasInitializer(integerLiteral())) +/// only match the declaration for a. +AST_MATCHER_P(Expr, ignoringParenCasts, internal::Matcher<Expr>, InnerMatcher) { + return InnerMatcher.matches(*Node.IgnoreParenCasts(), Finder, Builder); +} + +/// \brief Matches expressions that match InnerMatcher after implicit casts and +/// parentheses are stripped off. +/// +/// Explicit casts are not discarded. +/// Given +/// int arr[5]; +/// int a = 0; +/// char b = (0); +/// const int c = a; +/// int *d = (arr); +/// long e = ((long) 0l); +/// The matchers +/// variable(hasInitializer(ignoringParenImpCasts( +/// integerLiteral()))) +/// variable(hasInitializer(ignoringParenImpCasts( +/// declarationReference()))) +/// would match the declarations for a, b, c, and d, but not e. +/// while +/// variable(hasInitializer(integerLiteral())) +/// variable(hasInitializer(declarationReference())) +/// would only match the declaration for a. +AST_MATCHER_P(Expr, ignoringParenImpCasts, + internal::Matcher<Expr>, InnerMatcher) { + return InnerMatcher.matches(*Node.IgnoreParenImpCasts(), Finder, Builder); +} + /// \brief Matches classTemplateSpecializations where the n'th TemplateArgument /// matches the given Matcher. /// @@ -691,6 +761,19 @@ const internal::VariadicDynCastAllOfMatcher< Expr, ImplicitCastExpr> implicitCast; +/// \brief Matches any cast nodes of Clang's AST. +/// +/// Example: castExpr() matches each of the following: +/// (int) 3; +/// const_cast<Expr *>(SubExpr); +/// char c = 0; +/// but does not match +/// int i = (0); +/// int k = 0; +const internal::VariadicDynCastAllOfMatcher< + Expr, + CastExpr> castExpr; + /// \brief Matches functional cast expressions /// /// Example: Matches Foo(bar); @@ -1193,6 +1276,21 @@ AST_MATCHER_P(DeclRefExpr, throughUsingDecl, return false; } +/// \brief Matches the Decl of a DeclStmt which has a single declaration. +/// +/// Given +/// int a, b; +/// int c; +/// declarationStatement(hasSingleDecl(anything())) +/// matches 'int c;' but not 'int a, b;'. +AST_MATCHER_P(DeclStmt, hasSingleDecl, internal::Matcher<Decl>, InnerMatcher) { + if (Node.isSingleDecl()) { + const Decl *FoundDecl = Node.getSingleDecl(); + return InnerMatcher.matches(*FoundDecl, Finder, Builder); + } + return false; +} + /// \brief Matches a variable declaration that has an initializer expression /// that matches the given matcher. /// @@ -1238,6 +1336,44 @@ AST_POLYMORPHIC_MATCHER_P2( *Node.getArg(N)->IgnoreParenImpCasts(), Finder, Builder)); } +/// \brief Matches declaration statements that contain a specific number of +/// declarations. +/// +/// Example: Given +/// int a, b; +/// int c; +/// int d = 2, e; +/// declCountIs(2) +/// matches 'int a, b;' and 'int d = 2, e;', but not 'int c;'. +AST_MATCHER_P(DeclStmt, declCountIs, unsigned, N) { + return std::distance(Node.decl_begin(), Node.decl_end()) == N; +} + +/// \brief Matches the n'th declaration of a declaration statement. +/// +/// Note that this does not work for global declarations because the AST +/// breaks up multiple-declaration DeclStmt's into multiple single-declaration +/// DeclStmt's. +/// Example: Given non-global declarations +/// int a, b = 0; +/// int c; +/// int d = 2, e; +/// declarationStatement(containsDeclaration( +/// 0, variable(hasInitializer(anything())))) +/// matches only 'int d = 2, e;', and +/// declarationStatement(containsDeclaration(1, variable())) +/// matches 'int a, b = 0' as well as 'int d = 2, e;' +/// but 'int c;' is not matched. +AST_MATCHER_P2(DeclStmt, containsDeclaration, unsigned, N, + internal::Matcher<Decl>, InnerMatcher) { + const unsigned NumDecls = std::distance(Node.decl_begin(), Node.decl_end()); + if (N >= NumDecls) + return false; + DeclStmt::const_decl_iterator Iterator = Node.decl_begin(); + std::advance(Iterator, N); + return InnerMatcher.matches(**Iterator, Finder, Builder); +} + /// \brief Matches a constructor initializer. /// /// Given @@ -1385,6 +1521,18 @@ AST_MATCHER_P(FunctionDecl, returns, internal::Matcher<QualType>, Matcher) { return Matcher.matches(Node.getResultType(), Finder, Builder); } +/// \brief Matches extern "C" function declarations. +/// +/// Given: +/// extern "C" void f() {} +/// extern "C" { void g() {} } +/// void h() {} +/// function(isExternC()) +/// matches the declaration of f and g, but not the declaration h +AST_MATCHER(FunctionDecl, isExternC) { + return Node.isExternC(); +} + /// \brief Matches the condition expression of an if statement, for loop, /// or conditional operator. /// diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 99180e450e65..fade83ef9361 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -826,6 +826,27 @@ def SharedLocksRequired : InheritableAttr { let TemplateDependent = 1; } +// Type safety attributes for `void *' pointers and type tags. + +def ArgumentWithTypeTag : InheritableAttr { + let Spellings = [GNU<"argument_with_type_tag">, + GNU<"pointer_with_type_tag">]; + let Args = [IdentifierArgument<"ArgumentKind">, + UnsignedArgument<"ArgumentIdx">, + UnsignedArgument<"TypeTagIdx">, + BoolArgument<"IsPointer">]; + let Subjects = [Function]; +} + +def TypeTagForDatatype : InheritableAttr { + let Spellings = [GNU<"type_tag_for_datatype">]; + let Args = [IdentifierArgument<"ArgumentKind">, + TypeArgument<"MatchingCType">, + BoolArgument<"LayoutCompatible">, + BoolArgument<"MustBeNull">]; + let Subjects = [Var]; +} + // Microsoft-related attributes def MsStruct : InheritableAttr { diff --git a/include/clang/Basic/Builtins.def b/include/clang/Basic/Builtins.def index 1b060a509232..84b28811728e 100644 --- a/include/clang/Basic/Builtins.def +++ b/include/clang/Basic/Builtins.def @@ -376,9 +376,9 @@ BUILTIN(__builtin_ctz , "iUi" , "nc") BUILTIN(__builtin_ctzl , "iULi" , "nc") BUILTIN(__builtin_ctzll, "iULLi", "nc") // TODO: int ctzimax(uintmax_t) -BUILTIN(__builtin_ffs , "iUi" , "nc") -BUILTIN(__builtin_ffsl , "iULi" , "nc") -BUILTIN(__builtin_ffsll, "iULLi", "nc") +BUILTIN(__builtin_ffs , "ii" , "nc") +BUILTIN(__builtin_ffsl , "iLi" , "nc") +BUILTIN(__builtin_ffsll, "iLLi", "nc") BUILTIN(__builtin_parity , "iUi" , "nc") BUILTIN(__builtin_parityl , "iULi" , "nc") BUILTIN(__builtin_parityll, "iULLi", "nc") diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index b95a90bd21c3..d8632dd8c39d 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -155,6 +155,8 @@ def MethodAccess : DiagGroup<"objc-method-access">; def ObjCReceiver : DiagGroup<"receiver-expr">; def OverlengthStrings : DiagGroup<"overlength-strings">; def OverloadedVirtual : DiagGroup<"overloaded-virtual">; +def PrivateExtern : DiagGroup<"private-extern">; +def SelTypeCast : DiagGroup<"cast-of-sel-type">; def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">; def ObjCPropertyNoAttribute : DiagGroup<"objc-property-no-attribute">; def ObjCMissingSuperCalls : DiagGroup<"objc-missing-super-calls">; @@ -341,6 +343,8 @@ def FormatNonLiteral : DiagGroup<"format-nonliteral", [FormatSecurity]>; def Format2 : DiagGroup<"format=2", [FormatNonLiteral, FormatSecurity, FormatY2K]>; +def TypeSafety : DiagGroup<"type-safety">; + def Extra : DiagGroup<"extra", [ MissingFieldInitializers, IgnoredQualifiers, @@ -371,7 +375,9 @@ def Most : DiagGroup<"most", [ Unused, VolatileRegisterVar, ObjCMissingSuperCalls, - OverloadedVirtual + OverloadedVirtual, + PrivateExtern, + SelTypeCast ]>; // Thread Safety warnings diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 8cb82fd4a914..b1c16fa8529f 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -677,6 +677,10 @@ def warn_availability_and_unavailable : Warning< "'unavailable' availability overrides all other availability information">, InGroup<Availability>; +// Type safety attributes +def err_type_safety_unknown_flag : Error< + "invalid comparison flag %0; use 'layout_compatible' or 'must_be_null'">; + // Language specific pragmas // - Generic warnings def warn_pragma_expected_lparen : Warning< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 6d7b52e55f58..2d63dc42fd94 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -815,7 +815,7 @@ def err_friend_def_in_local_class : Error< "friend function cannot be defined in a local class">; def err_abstract_type_in_decl : Error< - "%select{return|parameter|variable|field}0 type %1 is an abstract class">; + "%select{return|parameter|variable|field|ivar}0 type %1 is an abstract class">; def err_allocation_of_abstract_type : Error< "allocating an object of abstract class type %0">; def err_throw_abstract_type : Error< @@ -1489,6 +1489,11 @@ def note_non_literal_user_provided_dtor : Note< "%0 is not literal because it has a user-provided destructor">; def note_non_literal_nontrivial_dtor : Note< "%0 is not literal because it has a non-trivial destructor">; +def warn_private_extern : Warning< + "Use of __private_extern__ on tentative definition has unexpected" + " behaviour - use __attribute__((visibility(\"hidden\"))) on extern" + " declaration or definition instead">, + InGroup<PrivateExtern>, DefaultIgnore; // C++11 char16_t/char32_t def warn_cxx98_compat_unicode_type : Warning< @@ -1542,6 +1547,8 @@ def err_attribute_argument_n_not_int : Error< "'%0' attribute requires parameter %1 to be an integer constant">; def err_attribute_argument_n_not_string : Error< "'%0' attribute requires parameter %1 to be a string">; +def err_attribute_argument_n_not_identifier : Error< + "'%0' attribute requires parameter %1 to be an identifier">; def err_attribute_argument_out_of_bounds : Error< "'%0' attribute parameter %1 is out of bounds">; def err_attribute_requires_objc_interface : Error< @@ -1550,6 +1557,8 @@ def err_attribute_uuid_malformed_guid : Error< "uuid attribute contains a malformed GUID">; def warn_nonnull_pointers_only : Warning< "nonnull attribute only applies to pointer arguments">; +def err_attribute_pointers_only : Error< + "'%0' attribute only applies to pointer arguments">; def err_attribute_invalid_implicit_this_argument : Error< "'%0' attribute is invalid for the implicit this argument">; def err_ownership_type : Error< @@ -1765,7 +1774,6 @@ def err_attribute_can_be_applied_only_to_value_decl : Error< def warn_attribute_not_on_decl : Error< "%0 attribute ignored when parsing type">; - // Availability attribute def warn_availability_unknown_platform : Warning< "unknown platform %0 in availability macro">, InGroup<Availability>; @@ -3677,6 +3685,10 @@ def err_illegal_decl_array_of_references : Error< "'%0' declared as array of references of type %1">; def err_decl_negative_array_size : Error< "'%0' declared as an array with a negative size">; +def err_array_static_outside_prototype : Error< + "%0 used in array declarator outside of function prototype">; +def err_array_static_not_outermost : Error< + "%0 used in non-outermost array type derivation">; def err_array_star_outside_prototype : Error< "star modifier used outside of function prototype">; def err_illegal_decl_pointer_to_reference : Error< @@ -4953,6 +4965,9 @@ def err_typecheck_cast_to_union_no_type : Error< "cast to union type from type %0 not present in union">; def err_cast_pointer_from_non_pointer_int : Error< "operand of type %0 cannot be cast to a pointer type">; +def warn_cast_pointer_from_sel : Warning< + "cast of type %0 to %1 is deprecated; use sel_getName instead">, + InGroup<SelTypeCast>; def err_cast_pointer_to_non_pointer_int : Error< "pointer cannot be cast to type %0">; def err_typecheck_expect_scalar_operand : Error< @@ -5467,6 +5482,23 @@ def warn_identity_field_assign : Warning< "assigning %select{field|instance variable}0 to itself">, InGroup<SelfAssignmentField>; +// Type safety attributes +def err_type_tag_for_datatype_not_ice : Error< + "'type_tag_for_datatype' attribute requires the initializer to be " + "an %select{integer|integral}0 constant expression">; +def err_type_tag_for_datatype_too_large : Error< + "'type_tag_for_datatype' attribute requires the initializer to be " + "an %select{integer|integral}0 constant expression " + "that can be represented by a 64 bit integer">; +def warn_type_tag_for_datatype_wrong_kind : Warning< + "this type tag was not designed to be used with this function">, + InGroup<TypeSafety>; +def warn_type_safety_type_mismatch : Warning< + "argument type %0 doesn't match specified '%1' type tag " + "%select{that requires %3|}2">, InGroup<TypeSafety>; +def warn_type_safety_null_pointer_required : Warning< + "specified %0 type tag requires a null pointer">, InGroup<TypeSafety>; + // Generic selections. def err_assoc_type_incomplete : Error< "type %0 in generic association incomplete">; diff --git a/include/clang/Lex/PTHManager.h b/include/clang/Lex/PTHManager.h index 25a49038a863..44f9ab39f321 100644 --- a/include/clang/Lex/PTHManager.h +++ b/include/clang/Lex/PTHManager.h @@ -101,7 +101,7 @@ class PTHManager : public IdentifierInfoLookup { public: // The current PTH version. - enum { Version = 9 }; + enum { Version = 10 }; ~PTHManager(); diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 353b59e0ae32..4ef92f7dbc69 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1834,6 +1834,10 @@ private: ParsedAttributes &Attrs, SourceLocation *EndLoc); + void ParseTypeTagForDatatypeAttribute(IdentifierInfo &AttrName, + SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, + SourceLocation *EndLoc); void ParseTypeofSpecifier(DeclSpec &DS); SourceLocation ParseDecltypeSpecifier(DeclSpec &DS); diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 5239044e6708..bf358862b09e 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -19,6 +19,7 @@ #include "llvm/ADT/SmallVector.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/VersionTuple.h" +#include "clang/Sema/Ownership.h" #include <cassert> namespace clang { @@ -87,6 +88,10 @@ private: /// availability attribute. unsigned IsAvailability : 1; + /// True if this has extra information associated with a + /// type_tag_for_datatype attribute. + unsigned IsTypeTagForDatatype : 1; + unsigned AttrKind : 8; /// \brief The location of the 'unavailable' keyword in an @@ -119,6 +124,22 @@ private: return reinterpret_cast<const AvailabilityChange*>(this+1)[index]; } +public: + struct TypeTagForDatatypeData { + ParsedType *MatchingCType; + unsigned LayoutCompatible : 1; + unsigned MustBeNull : 1; + }; + +private: + TypeTagForDatatypeData &getTypeTagForDatatypeDataSlot() { + return *reinterpret_cast<TypeTagForDatatypeData *>(this + 1); + } + + const TypeTagForDatatypeData &getTypeTagForDatatypeDataSlot() const { + return *reinterpret_cast<const TypeTagForDatatypeData *>(this + 1); + } + AttributeList(const AttributeList &); // DO NOT IMPLEMENT void operator=(const AttributeList &); // DO NOT IMPLEMENT void operator delete(void *); // DO NOT IMPLEMENT @@ -126,6 +147,7 @@ private: size_t allocated_size() const; + /// Constructor for attributes with expression arguments. AttributeList(IdentifierInfo *attrName, SourceRange attrRange, IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *parmName, SourceLocation parmLoc, @@ -134,12 +156,13 @@ private: : AttrName(attrName), ScopeName(scopeName), ParmName(parmName), AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false), - UsedAsTypeAttr(false), IsAvailability(false), - NextInPosition(0), NextInPool(0) { + UsedAsTypeAttr(false), IsAvailability(false), + IsTypeTagForDatatype(false), NextInPosition(0), NextInPool(0) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*)); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } + /// Constructor for availability attributes. AttributeList(IdentifierInfo *attrName, SourceRange attrRange, IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *parmName, SourceLocation parmLoc, @@ -153,6 +176,7 @@ private: AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(parmLoc), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), + IsTypeTagForDatatype(false), UnavailableLoc(unavailable), MessageExpr(messageExpr), NextInPosition(0), NextInPool(0) { new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); @@ -161,6 +185,25 @@ private: AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } + /// Constructor for type_tag_for_datatype attribute. + AttributeList(IdentifierInfo *attrName, SourceRange attrRange, + IdentifierInfo *scopeName, SourceLocation scopeLoc, + IdentifierInfo *argumentKindName, + SourceLocation argumentKindLoc, + ParsedType matchingCType, bool layoutCompatible, + bool mustBeNull, Syntax syntaxUsed) + : AttrName(attrName), ScopeName(scopeName), ParmName(argumentKindName), + AttrRange(attrRange), ScopeLoc(scopeLoc), ParmLoc(argumentKindLoc), + NumArgs(0), SyntaxUsed(syntaxUsed), + Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), + IsTypeTagForDatatype(true), NextInPosition(NULL), NextInPool(NULL) { + TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); + new (&ExtraData.MatchingCType) ParsedType(matchingCType); + ExtraData.LayoutCompatible = layoutCompatible; + ExtraData.MustBeNull = mustBeNull; + AttrKind = getKind(getName(), getScopeName(), syntaxUsed); + } + friend class AttributePool; friend class AttributeFactory; @@ -279,6 +322,24 @@ public: assert(getKind() == AT_Availability && "Not an availability attribute"); return MessageExpr; } + + const ParsedType &getMatchingCType() const { + assert(getKind() == AT_TypeTagForDatatype && + "Not a type_tag_for_datatype attribute"); + return *getTypeTagForDatatypeDataSlot().MatchingCType; + } + + bool getLayoutCompatible() const { + assert(getKind() == AT_TypeTagForDatatype && + "Not a type_tag_for_datatype attribute"); + return getTypeTagForDatatypeDataSlot().LayoutCompatible; + } + + bool getMustBeNull() const { + assert(getKind() == AT_TypeTagForDatatype && + "Not a type_tag_for_datatype attribute"); + return getTypeTagForDatatypeDataSlot().MustBeNull; + } }; /// A factory, from which one makes pools, from which one creates @@ -294,7 +355,11 @@ public: AvailabilityAllocSize = sizeof(AttributeList) + ((3 * sizeof(AvailabilityChange) + sizeof(void*) - 1) - / sizeof(void*) * sizeof(void*)) + / sizeof(void*) * sizeof(void*)), + TypeTagForDatatypeAllocSize = + sizeof(AttributeList) + + (sizeof(AttributeList::TypeTagForDatatypeData) + sizeof(void *) - 1) + / sizeof(void*) * sizeof(void*) }; private: @@ -411,6 +476,21 @@ public: AttributeList *createIntegerAttribute(ASTContext &C, IdentifierInfo *Name, SourceLocation TokLoc, int Arg); + + AttributeList *createTypeTagForDatatype( + IdentifierInfo *attrName, SourceRange attrRange, + IdentifierInfo *scopeName, SourceLocation scopeLoc, + IdentifierInfo *argumentKindName, + SourceLocation argumentKindLoc, + ParsedType matchingCType, bool layoutCompatible, + bool mustBeNull, AttributeList::Syntax syntax) { + void *memory = allocate(AttributeFactory::TypeTagForDatatypeAllocSize); + return add(new (memory) AttributeList(attrName, attrRange, + scopeName, scopeLoc, + argumentKindName, argumentKindLoc, + matchingCType, layoutCompatible, + mustBeNull, syntax)); + } }; /// addAttributeLists - Add two AttributeLists together @@ -503,7 +583,7 @@ public: /// dependencies on this method, it may not be long-lived. AttributeList *&getListRef() { return list; } - + /// Add attribute with expression arguments. AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange, IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *parmName, SourceLocation parmLoc, @@ -516,6 +596,7 @@ public: return attr; } + /// Add availability attribute. AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange, IdentifierInfo *scopeName, SourceLocation scopeLoc, IdentifierInfo *parmName, SourceLocation parmLoc, @@ -533,6 +614,24 @@ public: return attr; } + /// Add type_tag_for_datatype attribute. + AttributeList *addNewTypeTagForDatatype( + IdentifierInfo *attrName, SourceRange attrRange, + IdentifierInfo *scopeName, SourceLocation scopeLoc, + IdentifierInfo *argumentKindName, + SourceLocation argumentKindLoc, + ParsedType matchingCType, bool layoutCompatible, + bool mustBeNull, AttributeList::Syntax syntax) { + AttributeList *attr = + pool.createTypeTagForDatatype(attrName, attrRange, + scopeName, scopeLoc, + argumentKindName, argumentKindLoc, + matchingCType, layoutCompatible, + mustBeNull, syntax); + add(attr); + return attr; + } + AttributeList *addNewInteger(ASTContext &C, IdentifierInfo *name, SourceLocation loc, int arg) { AttributeList *attr = diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 058e0d9d3920..acc88c0578ed 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2498,13 +2498,11 @@ public: ExprResult CheckObjCForCollectionOperand(SourceLocation forLoc, Expr *collection); StmtResult ActOnObjCForCollectionStmt(SourceLocation ForColLoc, - SourceLocation LParenLoc, Stmt *First, Expr *collection, SourceLocation RParenLoc); StmtResult FinishObjCForCollectionStmt(Stmt *ForCollection, Stmt *Body); - StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc, - SourceLocation LParenLoc, Stmt *LoopVar, + StmtResult ActOnCXXForRangeStmt(SourceLocation ForLoc, Stmt *LoopVar, SourceLocation ColonLoc, Expr *Collection, SourceLocation RParenLoc); StmtResult BuildCXXForRangeStmt(SourceLocation ForLoc, @@ -2542,8 +2540,8 @@ public: bool MSAsm = false); StmtResult ActOnMSAsmStmt(SourceLocation AsmLoc, + SourceLocation LBraceLoc, ArrayRef<Token> AsmToks, - ArrayRef<unsigned> LineEnds, SourceLocation EndLoc); VarDecl *BuildObjCExceptionDecl(TypeSourceInfo *TInfo, QualType ExceptionType, @@ -4463,6 +4461,7 @@ public: AbstractParamType, AbstractVariableType, AbstractFieldType, + AbstractIvarType, AbstractArrayType }; @@ -7146,6 +7145,42 @@ private: void CheckBitFieldInitialization(SourceLocation InitLoc, FieldDecl *Field, Expr *Init); +public: + /// \brief Register a magic integral constant to be used as a type tag. + void RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind, + uint64_t MagicValue, QualType Type, + bool LayoutCompatible, bool MustBeNull); + + struct TypeTagData { + TypeTagData() {} + + TypeTagData(QualType Type, bool LayoutCompatible, bool MustBeNull) : + Type(Type), LayoutCompatible(LayoutCompatible), + MustBeNull(MustBeNull) + {} + + QualType Type; + + /// If true, \c Type should be compared with other expression's types for + /// layout-compatibility. + unsigned LayoutCompatible : 1; + unsigned MustBeNull : 1; + }; + + /// A pair of ArgumentKind identifier and magic value. This uniquely + /// identifies the magic value. + typedef std::pair<const IdentifierInfo *, uint64_t> TypeTagMagicValue; + +private: + /// \brief A map from magic value to type information. + OwningPtr<llvm::DenseMap<TypeTagMagicValue, TypeTagData> > + TypeTagForDatatypeMagicValues; + + /// \brief Peform checks on a call of a function with argument_with_type_tag + /// or pointer_with_type_tag attributes. + void CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr, + const Expr * const *ExprArgs); + /// \brief The parser's current scope. /// /// The parser maintains this state here. diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index 5ee52cc61589..48393a379b39 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -81,15 +81,19 @@ protected: typedef llvm::DenseSet<SymbolRef> Symbols; typedef llvm::DenseSet<const MemRegion *> Regions; - /// A set of symbols that are registered with this report as being + /// A (stack of) a set of symbols that are registered with this + /// report as being "interesting", and thus used to help decide which + /// diagnostics to include when constructing the final path diagnostic. + /// The stack is largely used by BugReporter when generating PathDiagnostics + /// for multiple PathDiagnosticConsumers. + llvm::SmallVector<Symbols *, 2> interestingSymbols; + + /// A (stack of) set of regions that are registered with this report as being /// "interesting", and thus used to help decide which diagnostics /// to include when constructing the final path diagnostic. - Symbols interestingSymbols; - - /// A set of regions that are registered with this report as being - /// "interesting", and thus used to help decide which diagnostics - /// to include when constructing the final path diagnostic. - Regions interestingRegions; + /// The stack is largely used by BugReporter when generating PathDiagnostics + /// for multiple PathDiagnosticConsumers. + llvm::SmallVector<Regions *, 2> interestingRegions; /// A set of custom visitors which generate "event" diagnostics at /// interesting points in the path. @@ -107,6 +111,15 @@ protected: /// when reporting an issue. bool DoNotPrunePath; +private: + // Used internally by BugReporter. + Symbols &getInterestingSymbols(); + Regions &getInterestingRegions(); + + void lazyInitializeInterestingSets(); + void pushInterestingSymbolsAndRegions(); + void popInterestingSymbolsAndRegions(); + public: BugReport(BugType& bt, StringRef desc, const ExplodedNode *errornode) : BT(bt), DeclWithIssue(0), Description(desc), ErrorNode(errornode), @@ -160,9 +173,9 @@ public: void markInteresting(const MemRegion *R); void markInteresting(SVal V); - bool isInteresting(SymbolRef sym) const; - bool isInteresting(const MemRegion *R) const; - bool isInteresting(SVal V) const; + bool isInteresting(SymbolRef sym); + bool isInteresting(const MemRegion *R); + bool isInteresting(SVal V); unsigned getConfigurationChangeToken() const { return ConfigurationChangeToken; @@ -295,7 +308,7 @@ class BugReporterData { public: virtual ~BugReporterData(); virtual DiagnosticsEngine& getDiagnostic() = 0; - virtual PathDiagnosticConsumer* getPathDiagnosticConsumer() = 0; + virtual ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() = 0; virtual ASTContext &getASTContext() = 0; virtual SourceManager& getSourceManager() = 0; }; @@ -318,6 +331,12 @@ private: /// Generate and flush the diagnostics for the given bug report. void FlushReport(BugReportEquivClass& EQ); + /// Generate and flush the diagnostics for the given bug report + /// and PathDiagnosticConsumer. + void FlushReport(BugReport *exampleReport, + PathDiagnosticConsumer &PD, + ArrayRef<BugReport*> BugReports); + /// The set of bug reports tracked by the BugReporter. llvm::FoldingSet<BugReportEquivClass> EQClasses; /// A vector of BugReports for tracking the allocated pointers and cleanup. @@ -341,8 +360,8 @@ public: return D.getDiagnostic(); } - PathDiagnosticConsumer* getPathDiagnosticConsumer() { - return D.getPathDiagnosticConsumer(); + ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() { + return D.getPathDiagnosticConsumers(); } /// \brief Iterator over the set of BugTypes tracked by the BugReporter. @@ -360,7 +379,8 @@ public: SourceManager& getSourceManager() { return D.getSourceManager(); } virtual void GeneratePathDiagnostic(PathDiagnostic& pathDiagnostic, - SmallVectorImpl<BugReport *> &bugReports) {} + PathDiagnosticConsumer &PC, + ArrayRef<BugReport *> &bugReports) {} void Register(BugType *BT); @@ -421,7 +441,8 @@ public: ProgramStateManager &getStateManager(); virtual void GeneratePathDiagnostic(PathDiagnostic &pathDiagnostic, - SmallVectorImpl<BugReport*> &bugReports); + PathDiagnosticConsumer &PC, + ArrayRef<BugReport*> &bugReports); /// classof - Used by isa<>, cast<>, and dyn_cast<>. static bool classof(const BugReporter* R) { diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h index 2e7abfa5cc3b..973cfb109c05 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -51,22 +51,25 @@ typedef const SymExpr* SymbolRef; class PathDiagnostic; class PathDiagnosticConsumer { +public: + typedef std::vector<std::pair<StringRef, std::string> > FilesMade; + +private: virtual void anchor(); public: PathDiagnosticConsumer() : flushed(false) {} virtual ~PathDiagnosticConsumer(); - void FlushDiagnostics(SmallVectorImpl<std::string> *FilesMade); + void FlushDiagnostics(FilesMade *FilesMade); virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade) - = 0; + FilesMade *filesMade) = 0; virtual StringRef getName() const = 0; void HandlePathDiagnostic(PathDiagnostic *D); - enum PathGenerationScheme { Minimal, Extensive }; + enum PathGenerationScheme { None, Minimal, Extensive }; virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } virtual bool supportsLogicalOpControlFlow() const { return false; } virtual bool supportsAllBlockEdges() const { return false; } @@ -332,15 +335,8 @@ public: ranges.push_back(SourceRange(B,E)); } - typedef const SourceRange* range_iterator; - - range_iterator ranges_begin() const { - return ranges.empty() ? NULL : &ranges[0]; - } - - range_iterator ranges_end() const { - return ranges_begin() + ranges.size(); - } + /// Return the SourceRanges associated with this PathDiagnosticPiece. + ArrayRef<SourceRange> getRanges() const { return ranges; } static inline bool classof(const PathDiagnosticPiece *P) { return true; diff --git a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h index 65be3a406b43..3aab648dc574 100644 --- a/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h +++ b/include/clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_GR_PATH_DIAGNOSTIC_CLIENTS_H #include <string> +#include <vector> namespace clang { @@ -23,24 +24,25 @@ class Preprocessor; namespace ento { class PathDiagnosticConsumer; +typedef std::vector<PathDiagnosticConsumer*> PathDiagnosticConsumers; -PathDiagnosticConsumer* -createHTMLDiagnosticConsumer(const std::string& prefix, const Preprocessor &PP); +void createHTMLDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string& prefix, + const Preprocessor &PP); -PathDiagnosticConsumer* -createPlistDiagnosticConsumer(const std::string& prefix, const Preprocessor &PP, - PathDiagnosticConsumer *SubPD = 0); +void createPlistDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string& prefix, + const Preprocessor &PP); -PathDiagnosticConsumer* -createPlistMultiFileDiagnosticConsumer(const std::string& prefix, - const Preprocessor &PP); +void createPlistMultiFileDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string& prefix, + const Preprocessor &PP); -PathDiagnosticConsumer* -createTextPathDiagnosticConsumer(const std::string& prefix, - const Preprocessor &PP); +void createTextPathDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string& prefix, + const Preprocessor &PP); -} // end GR namespace - -} // end clang namespace +} // end 'ento' namespace +} // end 'clang' namespace #endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h index 1cc53d4423da..876196ba4f7f 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h @@ -19,6 +19,7 @@ #include "clang/Frontend/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" namespace clang { @@ -32,8 +33,7 @@ class AnalysisManager : public BugReporterData { ASTContext &Ctx; DiagnosticsEngine &Diags; const LangOptions &LangOpts; - - OwningPtr<PathDiagnosticConsumer> PD; + PathDiagnosticConsumers PathConsumers; // Configurable components creators. StoreManagerCreator CreateStoreMgr; @@ -82,8 +82,9 @@ public: bool NoRetryExhausted; public: - AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, - const LangOptions &lang, PathDiagnosticConsumer *pd, + AnalysisManager(ASTContext &ctx,DiagnosticsEngine &diags, + const LangOptions &lang, + const PathDiagnosticConsumers &Consumers, StoreManagerCreator storemgr, ConstraintManagerCreator constraintmgr, CheckerManager *checkerMgr, @@ -99,12 +100,7 @@ public: AnalysisInliningMode inliningMode, bool NoRetry); - /// Construct a clone of the given AnalysisManager with the given ASTContext - /// and DiagnosticsEngine. - AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, - AnalysisManager &ParentAM); - - ~AnalysisManager() { FlushDiagnostics(); } + ~AnalysisManager(); void ClearContexts() { AnaCtxMgr.clear(); @@ -140,15 +136,12 @@ public: return LangOpts; } - virtual PathDiagnosticConsumer *getPathDiagnosticConsumer() { - return PD.get(); - } - - void FlushDiagnostics() { - if (PD.get()) - PD->FlushDiagnostics(0); + ArrayRef<PathDiagnosticConsumer*> getPathDiagnosticConsumers() { + return PathConsumers; } + void FlushDiagnostics(); + unsigned getMaxNodes() const { return MaxNodes; } unsigned getMaxVisit() const { return MaxVisit; } diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 47edfe9fca91..f6c5830c2955 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -328,7 +328,7 @@ public: // For debugging purposes only void dump(raw_ostream &Out) const; - LLVM_ATTRIBUTE_USED void dump() const { dump(llvm::errs()); } + LLVM_ATTRIBUTE_USED void dump() const; static bool classof(const CallEvent *) { return true; } }; @@ -804,8 +804,12 @@ public: return getOriginExpr()->getReceiverInterface(); } + /// Returns how the message was written in the source (property access, + /// subscript, or explicit message send). ObjCMessageKind getMessageKind() const; + /// Returns true if this property access or subscript is a setter (has the + /// form of an assignment). bool isSetter() const { switch (getMessageKind()) { case OCM_Message: diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 01218effff90..8044ed839a75 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -52,7 +52,9 @@ class RegionOffset { int64_t Offset; public: - enum { Symbolic = INT64_MAX }; + // We're using a const instead of an enumeration due to the size required; + // Visual Studio will only create enumerations of size int, not long long. + static const int64_t Symbolic = INT64_MAX; RegionOffset() : R(0) {} RegionOffset(const MemRegion *r, int64_t off) : R(r), Offset(off) {} diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index d36aa1bd1bcb..b0c51dd5b928 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -65,10 +65,14 @@ class DynamicTypeInfo { public: DynamicTypeInfo() : T(QualType()) {} - DynamicTypeInfo(QualType WithType, bool CanBeSub = true): - T(WithType), CanBeASubClass(CanBeSub) {} - QualType getType() { return T; } - bool canBeASubClass() { return CanBeASubClass; } + DynamicTypeInfo(QualType WithType, bool CanBeSub = true) + : T(WithType), CanBeASubClass(CanBeSub) {} + + bool isValid() const { return !T.isNull(); } + + QualType getType() const { return T; } + bool canBeASubClass() const { return CanBeASubClass; } + void Profile(llvm::FoldingSetNodeID &ID) const { T.Profile(ID); ID.AddInteger((unsigned)CanBeASubClass); diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp index a74ef14a9e2b..2d7c9bd7864a 100644 --- a/lib/AST/APValue.cpp +++ b/lib/AST/APValue.cpp @@ -367,8 +367,7 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{ if (const ValueDecl *VD = Base.dyn_cast<const ValueDecl*>()) Out << *VD; else - Base.get<const Expr*>()->printPretty(Out, Ctx, 0, - Ctx.getPrintingPolicy()); + Base.get<const Expr*>()->printPretty(Out, 0, Ctx.getPrintingPolicy()); if (!O.isZero()) { Out << " + " << (O / S); if (IsReference) @@ -389,7 +388,7 @@ void APValue::printPretty(raw_ostream &Out, ASTContext &Ctx, QualType Ty) const{ ElemTy = VD->getType(); } else { const Expr *E = Base.get<const Expr*>(); - E->printPretty(Out, Ctx, 0,Ctx.getPrintingPolicy()); + E->printPretty(Out, 0, Ctx.getPrintingPolicy()); ElemTy = E->getType(); } diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index ad48dffb4d4f..c02132329e6d 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -66,6 +66,12 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { if (D->isImplicit()) return NULL; + // User can not attach documentation to implicit instantiations. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) + return NULL; + } + // TODO: handle comments for function parameters properly. if (isa<ParmVarDecl>(D)) return NULL; @@ -145,7 +151,6 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { SourceMgr.getLineNumber(DeclLocDecomp.first, DeclLocDecomp.second) == SourceMgr.getLineNumber(CommentBeginDecomp.first, CommentBeginDecomp.second)) { - (*Comment)->setDecl(D); return *Comment; } } @@ -185,13 +190,13 @@ RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const { if (Text.find_first_of(",;{}#@") != StringRef::npos) return NULL; - (*Comment)->setDecl(D); return *Comment; } -const RawComment *ASTContext::getRawCommentForAnyRedecl(const Decl *D) const { - // If we have a 'templated' declaration for a template, adjust 'D' to - // refer to the actual template. +namespace { +/// If we have a 'templated' declaration for a template, adjust 'D' to +/// refer to the actual template. +const Decl *adjustDeclToTemplate(const Decl *D) { if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { if (const FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate()) D = FTD; @@ -200,6 +205,14 @@ const RawComment *ASTContext::getRawCommentForAnyRedecl(const Decl *D) const { D = CTD; } // FIXME: Alias templates? + return D; +} +} // unnamed namespace + +const RawComment *ASTContext::getRawCommentForAnyRedecl( + const Decl *D, + const Decl **OriginalDecl) const { + D = adjustDeclToTemplate(D); // Check whether we have cached a comment for this declaration already. { @@ -207,13 +220,17 @@ const RawComment *ASTContext::getRawCommentForAnyRedecl(const Decl *D) const { RedeclComments.find(D); if (Pos != RedeclComments.end()) { const RawCommentAndCacheFlags &Raw = Pos->second; - if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) + if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) { + if (OriginalDecl) + *OriginalDecl = Raw.getOriginalDecl(); return Raw.getRaw(); + } } } // Search for comments attached to declarations in the redeclaration chain. const RawComment *RC = NULL; + const Decl *OriginalDeclForRC = NULL; for (Decl::redecl_iterator I = D->redecls_begin(), E = D->redecls_end(); I != E; ++I) { @@ -223,16 +240,19 @@ const RawComment *ASTContext::getRawCommentForAnyRedecl(const Decl *D) const { const RawCommentAndCacheFlags &Raw = Pos->second; if (Raw.getKind() != RawCommentAndCacheFlags::NoCommentInDecl) { RC = Raw.getRaw(); + OriginalDeclForRC = Raw.getOriginalDecl(); break; } } else { RC = getRawCommentForDeclNoCache(*I); + OriginalDeclForRC = *I; RawCommentAndCacheFlags Raw; if (RC) { Raw.setRaw(RC); Raw.setKind(RawCommentAndCacheFlags::FromDecl); } else Raw.setKind(RawCommentAndCacheFlags::NoCommentInDecl); + Raw.setOriginalDecl(*I); RedeclComments[*I] = Raw; if (RC) break; @@ -242,10 +262,14 @@ const RawComment *ASTContext::getRawCommentForAnyRedecl(const Decl *D) const { // If we found a comment, it should be a documentation comment. assert(!RC || RC->isDocumentation()); + if (OriginalDecl) + *OriginalDecl = OriginalDeclForRC; + // Update cache for every declaration in the redeclaration chain. RawCommentAndCacheFlags Raw; Raw.setRaw(RC); Raw.setKind(RawCommentAndCacheFlags::FromRedecl); + Raw.setOriginalDecl(OriginalDeclForRC); for (Decl::redecl_iterator I = D->redecls_begin(), E = D->redecls_end(); @@ -259,11 +283,24 @@ const RawComment *ASTContext::getRawCommentForAnyRedecl(const Decl *D) const { } comments::FullComment *ASTContext::getCommentForDecl(const Decl *D) const { - const RawComment *RC = getRawCommentForAnyRedecl(D); + D = adjustDeclToTemplate(D); + const Decl *Canonical = D->getCanonicalDecl(); + llvm::DenseMap<const Decl *, comments::FullComment *>::iterator Pos = + ParsedComments.find(Canonical); + if (Pos != ParsedComments.end()) + return Pos->second; + + const Decl *OriginalDecl; + const RawComment *RC = getRawCommentForAnyRedecl(D, &OriginalDecl); if (!RC) return NULL; - return RC->getParsed(*this); + if (D != OriginalDecl) + return getCommentForDecl(OriginalDecl); + + comments::FullComment *FC = RC->parse(*this, D); + ParsedComments[Canonical] = FC; + return FC; } void @@ -5437,7 +5474,8 @@ ASTContext::getQualifiedTemplateName(NestedNameSpecifier *NNS, QualifiedTemplateName *QTN = QualifiedTemplateNames.FindNodeOrInsertPos(ID, InsertPos); if (!QTN) { - QTN = new (*this,4) QualifiedTemplateName(NNS, TemplateKeyword, Template); + QTN = new (*this, llvm::alignOf<QualifiedTemplateName>()) + QualifiedTemplateName(NNS, TemplateKeyword, Template); QualifiedTemplateNames.InsertNode(QTN, InsertPos); } @@ -5464,10 +5502,12 @@ ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS); if (CanonNNS == NNS) { - QTN = new (*this,4) DependentTemplateName(NNS, Name); + QTN = new (*this, llvm::alignOf<DependentTemplateName>()) + DependentTemplateName(NNS, Name); } else { TemplateName Canon = getDependentTemplateName(CanonNNS, Name); - QTN = new (*this,4) DependentTemplateName(NNS, Name, Canon); + QTN = new (*this, llvm::alignOf<DependentTemplateName>()) + DependentTemplateName(NNS, Name, Canon); DependentTemplateName *CheckQTN = DependentTemplateNames.FindNodeOrInsertPos(ID, InsertPos); assert(!CheckQTN && "Dependent type name canonicalization broken"); @@ -5498,10 +5538,12 @@ ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, NestedNameSpecifier *CanonNNS = getCanonicalNestedNameSpecifier(NNS); if (CanonNNS == NNS) { - QTN = new (*this,4) DependentTemplateName(NNS, Operator); + QTN = new (*this, llvm::alignOf<DependentTemplateName>()) + DependentTemplateName(NNS, Operator); } else { TemplateName Canon = getDependentTemplateName(CanonNNS, Operator); - QTN = new (*this,4) DependentTemplateName(NNS, Operator, Canon); + QTN = new (*this, llvm::alignOf<DependentTemplateName>()) + DependentTemplateName(NNS, Operator, Canon); DependentTemplateName *CheckQTN = DependentTemplateNames.FindNodeOrInsertPos(ID, InsertPos); diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp index 35fcd41f26e5..a605f1a40cb3 100644 --- a/lib/AST/ASTDiagnostic.cpp +++ b/lib/AST/ASTDiagnostic.cpp @@ -1149,7 +1149,7 @@ class TemplateDiff { if (!E) OS << "(no argument)"; else - E->printPretty(OS, Context, 0, Policy); return; + E->printPretty(OS, 0, Policy); return; } /// PrintTemplateTemplate - Handles printing of template template arguments, diff --git a/lib/AST/CommentCommandTraits.cpp b/lib/AST/CommentCommandTraits.cpp index d8ce1f3fe7bd..dc7a0bd17532 100644 --- a/lib/AST/CommentCommandTraits.cpp +++ b/lib/AST/CommentCommandTraits.cpp @@ -15,9 +15,9 @@ namespace comments { // TODO: tablegen -bool CommandTraits::isVerbatimBlockCommand(StringRef BeginName, +bool CommandTraits::isVerbatimBlockCommand(StringRef StartName, StringRef &EndName) const { - const char *Result = llvm::StringSwitch<const char *>(BeginName) + const char *Result = llvm::StringSwitch<const char *>(StartName) .Case("code", "endcode") .Case("verbatim", "endverbatim") .Case("htmlonly", "endhtmlonly") @@ -44,7 +44,7 @@ bool CommandTraits::isVerbatimBlockCommand(StringRef BeginName, I = VerbatimBlockCommands.begin(), E = VerbatimBlockCommands.end(); I != E; ++I) - if (I->BeginName == BeginName) { + if (I->StartName == StartName) { EndName = I->EndName; return true; } @@ -115,10 +115,10 @@ bool CommandTraits::isDeclarationCommand(StringRef Name) const { .Default(false); } -void CommandTraits::addVerbatimBlockCommand(StringRef BeginName, +void CommandTraits::addVerbatimBlockCommand(StringRef StartName, StringRef EndName) { VerbatimBlockCommand VBC; - VBC.BeginName = BeginName; + VBC.StartName = StartName; VBC.EndName = EndName; VerbatimBlockCommands.push_back(VBC); } diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index eec2e9d3cf74..2f21e4cbd652 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -1294,15 +1294,20 @@ static bool recursivelyOverrides(const CXXMethodDecl *DerivedMD, } CXXMethodDecl * -CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD) { +CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD, + bool MayBeBase) { if (this->getParent()->getCanonicalDecl() == RD->getCanonicalDecl()) return this; // Lookup doesn't work for destructors, so handle them separately. if (isa<CXXDestructorDecl>(this)) { CXXMethodDecl *MD = RD->getDestructor(); - if (MD && recursivelyOverrides(MD, this)) - return MD; + if (MD) { + if (recursivelyOverrides(MD, this)) + return MD; + if (MayBeBase && recursivelyOverrides(this, MD)) + return MD; + } return NULL; } @@ -1313,6 +1318,8 @@ CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD) { continue; if (recursivelyOverrides(MD, this)) return MD; + if (MayBeBase && recursivelyOverrides(this, MD)) + return MD; } for (CXXRecordDecl::base_class_const_iterator I = RD->bases_begin(), diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index aad0ca1b536e..7f47604dece3 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -26,7 +26,6 @@ using namespace clang; namespace { class DeclPrinter : public DeclVisitor<DeclPrinter> { raw_ostream &Out; - ASTContext &Context; PrintingPolicy Policy; unsigned Indentation; bool PrintInstantiation; @@ -38,11 +37,9 @@ namespace { void Print(AccessSpecifier AS); public: - DeclPrinter(raw_ostream &Out, ASTContext &Context, - const PrintingPolicy &Policy, - unsigned Indentation = 0, - bool PrintInstantiation = false) - : Out(Out), Context(Context), Policy(Policy), Indentation(Indentation), + DeclPrinter(raw_ostream &Out, const PrintingPolicy &Policy, + unsigned Indentation = 0, bool PrintInstantiation = false) + : Out(Out), Policy(Policy), Indentation(Indentation), PrintInstantiation(PrintInstantiation) { } void VisitDeclContext(DeclContext *DC, bool Indent = true); @@ -96,7 +93,7 @@ void Decl::print(raw_ostream &Out, unsigned Indentation, void Decl::print(raw_ostream &Out, const PrintingPolicy &Policy, unsigned Indentation, bool PrintInstantiation) const { - DeclPrinter Printer(Out, getASTContext(), Policy, Indentation, PrintInstantiation); + DeclPrinter Printer(Out, Policy, Indentation, PrintInstantiation); Printer.Visit(const_cast<Decl*>(this)); } @@ -171,13 +168,17 @@ void DeclContext::dumpDeclContext() const { DC = DC->getParent(); ASTContext &Ctx = cast<TranslationUnitDecl>(DC)->getASTContext(); - DeclPrinter Printer(llvm::errs(), Ctx, Ctx.getPrintingPolicy(), 0); + DeclPrinter Printer(llvm::errs(), Ctx.getPrintingPolicy(), 0); Printer.VisitDeclContext(const_cast<DeclContext *>(this), /*Indent=*/false); } +void Decl::dump() const { + dump(llvm::errs()); +} + void Decl::dump(raw_ostream &Out) const { PrintingPolicy Policy = getASTContext().getPrintingPolicy(); - Policy.Dump = true; + Policy.DumpSourceManager = &getASTContext().getSourceManager(); print(Out, Policy, /*Indentation*/ 0, /*PrintInstantiation*/ true); } @@ -191,8 +192,8 @@ void DeclPrinter::prettyPrintAttributes(Decl *D) { if (D->hasAttrs()) { AttrVec &Attrs = D->getAttrs(); for (AttrVec::const_iterator i=Attrs.begin(), e=Attrs.end(); i!=e; ++i) { - Attr *A = *i; - A->printPretty(Out, Context); + Attr *A = *i; + A->printPretty(Out, Policy); } } } @@ -231,7 +232,7 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) { if (isa<ObjCIvarDecl>(*D)) continue; - if (!Policy.Dump) { + if (!Policy.DumpSourceManager) { // Skip over implicit declarations in pretty-printing mode. if (D->isImplicit()) continue; // FIXME: Ugly hack so we don't pretty-print the builtin declaration @@ -381,7 +382,7 @@ void DeclPrinter::VisitEnumConstantDecl(EnumConstantDecl *D) { Out << *D; if (Expr *Init = D->getInitExpr()) { Out << " = "; - Init->printPretty(Out, Context, 0, Policy, Indentation); + Init->printPretty(Out, 0, Policy, Indentation); } } @@ -420,7 +421,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { Proto += "("; if (FT) { llvm::raw_string_ostream POut(Proto); - DeclPrinter ParamPrinter(POut, Context, SubPolicy, Indentation); + DeclPrinter ParamPrinter(POut, SubPolicy, Indentation); for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) { if (i) POut << ", "; ParamPrinter.VisitParmVarDecl(D->getParamDecl(i)); @@ -466,7 +467,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { if (FT->getExceptionSpecType() == EST_ComputedNoexcept) { Proto += "("; llvm::raw_string_ostream EOut(Proto); - FT->getNoexceptExpr()->printPretty(EOut, Context, 0, SubPolicy, + FT->getNoexceptExpr()->printPretty(EOut, 0, SubPolicy, Indentation); EOut.flush(); Proto += EOut.str(); @@ -522,7 +523,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { SimpleInit = Init; if (SimpleInit) - SimpleInit->printPretty(Out, Context, 0, Policy, Indentation); + SimpleInit->printPretty(Out, 0, Policy, Indentation); else { for (unsigned I = 0; I != NumArgs; ++I) { if (isa<CXXDefaultArgExpr>(Args[I])) @@ -530,7 +531,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { if (I) Out << ", "; - Args[I]->printPretty(Out, Context, 0, Policy, Indentation); + Args[I]->printPretty(Out, 0, Policy, Indentation); } } } @@ -554,7 +555,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { // This is a K&R function definition, so we need to print the // parameters. Out << '\n'; - DeclPrinter ParamPrinter(Out, Context, SubPolicy, Indentation); + DeclPrinter ParamPrinter(Out, SubPolicy, Indentation); Indentation += Policy.Indentation; for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) { Indent(); @@ -565,7 +566,7 @@ void DeclPrinter::VisitFunctionDecl(FunctionDecl *D) { } else Out << ' '; - D->getBody()->printPretty(Out, Context, 0, SubPolicy, Indentation); + D->getBody()->printPretty(Out, 0, SubPolicy, Indentation); Out << '\n'; } } @@ -580,7 +581,7 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) { if (D->isBitField()) { Out << " : "; - D->getBitWidth()->printPretty(Out, Context, 0, Policy, Indentation); + D->getBitWidth()->printPretty(Out, 0, Policy, Indentation); } Expr *Init = D->getInClassInitializer(); @@ -589,7 +590,7 @@ void DeclPrinter::VisitFieldDecl(FieldDecl *D) { Out << " "; else Out << " = "; - Init->printPretty(Out, Context, 0, Policy, Indentation); + Init->printPretty(Out, 0, Policy, Indentation); } prettyPrintAttributes(D); } @@ -625,7 +626,7 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { else if (D->getInitStyle() == VarDecl::CInit) { Out << " = "; } - Init->printPretty(Out, Context, 0, Policy, Indentation); + Init->printPretty(Out, 0, Policy, Indentation); if (D->getInitStyle() == VarDecl::CallInit) Out << ")"; } @@ -639,7 +640,7 @@ void DeclPrinter::VisitParmVarDecl(ParmVarDecl *D) { void DeclPrinter::VisitFileScopeAsmDecl(FileScopeAsmDecl *D) { Out << "__asm ("; - D->getAsmString()->printPretty(Out, Context, 0, Policy, Indentation); + D->getAsmString()->printPretty(Out, 0, Policy, Indentation); Out << ")"; } @@ -650,9 +651,9 @@ void DeclPrinter::VisitImportDecl(ImportDecl *D) { void DeclPrinter::VisitStaticAssertDecl(StaticAssertDecl *D) { Out << "static_assert("; - D->getAssertExpr()->printPretty(Out, Context, 0, Policy, Indentation); + D->getAssertExpr()->printPretty(Out, 0, Policy, Indentation); Out << ", "; - D->getMessage()->printPretty(Out, Context, 0, Policy, Indentation); + D->getMessage()->printPretty(Out, 0, Policy, Indentation); Out << ")"; } @@ -786,8 +787,7 @@ void DeclPrinter::PrintTemplateParameters( Args->get(i).print(Policy, Out); } else if (NTTP->hasDefaultArgument()) { Out << " = "; - NTTP->getDefaultArgument()->printPretty(Out, Context, 0, Policy, - Indentation); + NTTP->getDefaultArgument()->printPretty(Out, 0, Policy, Indentation); } } else if (const TemplateTemplateParmDecl *TTPD = dyn_cast<TemplateTemplateParmDecl>(Param)) { @@ -871,7 +871,7 @@ void DeclPrinter::VisitObjCMethodDecl(ObjCMethodDecl *OMD) { if (OMD->getBody()) { Out << ' '; - OMD->getBody()->printPretty(Out, Context, 0, Policy); + OMD->getBody()->printPretty(Out, 0, Policy); Out << '\n'; } } diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index 5aebc2b764c0..a7e89994afe8 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -43,7 +43,8 @@ TemplateParameterList::Create(const ASTContext &C, SourceLocation TemplateLoc, unsigned NumParams, SourceLocation RAngleLoc) { unsigned Size = sizeof(TemplateParameterList) + sizeof(NamedDecl *) * NumParams; - unsigned Align = llvm::AlignOf<TemplateParameterList>::Alignment; + unsigned Align = std::max(llvm::alignOf<TemplateParameterList>(), + llvm::alignOf<NamedDecl*>()); void *Mem = C.Allocate(Size, Align); return new (Mem) TemplateParameterList(TemplateLoc, LAngleLoc, Params, NumParams, RAngleLoc); diff --git a/lib/AST/DumpXML.cpp b/lib/AST/DumpXML.cpp index c1432b5720c7..84f3fc491cb0 100644 --- a/lib/AST/DumpXML.cpp +++ b/lib/AST/DumpXML.cpp @@ -1022,12 +1022,17 @@ struct XMLDumper : public XMLDeclVisitor<XMLDumper>, }; } +void Decl::dumpXML() const { + dump(llvm::errs()); +} + void Decl::dumpXML(raw_ostream &out) const { XMLDumper(out, getASTContext()).dispatch(const_cast<Decl*>(this)); } #else /* ifndef NDEBUG */ +void Decl::dumpXML() const {} void Decl::dumpXML(raw_ostream &out) const {} #endif diff --git a/lib/AST/NestedNameSpecifier.cpp b/lib/AST/NestedNameSpecifier.cpp index dbf267bd65e0..49b119b8e05c 100644 --- a/lib/AST/NestedNameSpecifier.cpp +++ b/lib/AST/NestedNameSpecifier.cpp @@ -18,6 +18,7 @@ #include "clang/AST/PrettyPrinter.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" +#include "llvm/Support/AlignOf.h" #include "llvm/Support/raw_ostream.h" #include <cassert> @@ -33,7 +34,8 @@ NestedNameSpecifier::FindOrInsert(const ASTContext &Context, NestedNameSpecifier *NNS = Context.NestedNameSpecifiers.FindNodeOrInsertPos(ID, InsertPos); if (!NNS) { - NNS = new (Context, 4) NestedNameSpecifier(Mockup); + NNS = new (Context, llvm::alignOf<NestedNameSpecifier>()) + NestedNameSpecifier(Mockup); Context.NestedNameSpecifiers.InsertNode(NNS, InsertPos); } @@ -107,7 +109,9 @@ NestedNameSpecifier::Create(const ASTContext &Context, IdentifierInfo *II) { NestedNameSpecifier * NestedNameSpecifier::GlobalSpecifier(const ASTContext &Context) { if (!Context.GlobalNestedNameSpecifier) - Context.GlobalNestedNameSpecifier = new (Context, 4) NestedNameSpecifier(); + Context.GlobalNestedNameSpecifier = + new (Context, llvm::alignOf<NestedNameSpecifier>()) + NestedNameSpecifier(); return Context.GlobalNestedNameSpecifier; } @@ -630,4 +634,3 @@ NestedNameSpecifierLocBuilder::getWithLocInContext(ASTContext &Context) const { memcpy(Mem, Buffer, BufferSize); return NestedNameSpecifierLoc(Representation, Mem); } - diff --git a/lib/AST/RawCommentList.cpp b/lib/AST/RawCommentList.cpp index c704cabe69f7..a5a32870577a 100644 --- a/lib/AST/RawCommentList.cpp +++ b/lib/AST/RawCommentList.cpp @@ -65,7 +65,7 @@ bool mergedCommentIsTrailingComment(StringRef Comment) { RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR, bool Merged) : Range(SR), RawTextValid(false), BriefTextValid(false), - IsAlmostTrailingComment(false), + IsAttached(false), IsAlmostTrailingComment(false), BeginLineValid(false), EndLineValid(false) { // Extract raw comment text, if possible. if (SR.getBegin() == SR.getEnd() || getRawText(SourceMgr).empty()) { @@ -87,16 +87,6 @@ RawComment::RawComment(const SourceManager &SourceMgr, SourceRange SR, } } -const Decl *RawComment::getDecl() const { - if (DeclOrParsedComment.isNull()) - return NULL; - - if (const Decl *D = DeclOrParsedComment.dyn_cast<const Decl *>()) - return D; - - return DeclOrParsedComment.get<comments::FullComment *>()->getDecl(); -} - unsigned RawComment::getBeginLine(const SourceManager &SM) const { if (BeginLineValid) return BeginLine; @@ -169,7 +159,8 @@ const char *RawComment::extractBriefText(const ASTContext &Context) const { return BriefTextPtr; } -comments::FullComment *RawComment::parse(const ASTContext &Context) const { +comments::FullComment *RawComment::parse(const ASTContext &Context, + const Decl *D) const { // Make sure that RawText is valid. getRawText(Context.getSourceManager()); @@ -179,13 +170,11 @@ comments::FullComment *RawComment::parse(const ASTContext &Context) const { RawText.begin(), RawText.end()); comments::Sema S(Context.getAllocator(), Context.getSourceManager(), Context.getDiagnostics(), Traits); - S.setDecl(getDecl()); + S.setDecl(D); comments::Parser P(L, S, Context.getAllocator(), Context.getSourceManager(), Context.getDiagnostics(), Traits); - comments::FullComment *FC = P.parseFullComment(); - DeclOrParsedComment = FC; - return FC; + return P.parseFullComment(); } namespace { diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index d877c3fab762..77452c9d9d1c 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -584,22 +584,27 @@ AsmStmt::AsmStmt(ASTContext &C, SourceLocation asmloc, bool issimple, } MSAsmStmt::MSAsmStmt(ASTContext &C, SourceLocation asmloc, - bool issimple, bool isvolatile, ArrayRef<Token> asmtoks, - ArrayRef<unsigned> lineends, StringRef asmstr, + SourceLocation lbraceloc, bool issimple, bool isvolatile, + ArrayRef<Token> asmtoks, ArrayRef<IdentifierInfo*> inputs, + ArrayRef<IdentifierInfo*> outputs, StringRef asmstr, ArrayRef<StringRef> clobbers, SourceLocation endloc) - : Stmt(MSAsmStmtClass), AsmLoc(asmloc), EndLoc(endloc), + : Stmt(MSAsmStmtClass), AsmLoc(asmloc), LBraceLoc(lbraceloc), EndLoc(endloc), AsmStr(asmstr.str()), IsSimple(issimple), IsVolatile(isvolatile), - NumAsmToks(asmtoks.size()), NumLineEnds(lineends.size()), - NumClobbers(clobbers.size()) { + NumAsmToks(asmtoks.size()), NumInputs(inputs.size()), + NumOutputs(outputs.size()), NumClobbers(clobbers.size()) { + + unsigned NumExprs = NumOutputs + NumInputs; + + Names = new (C) IdentifierInfo*[NumExprs]; + for (unsigned i = 0, e = NumOutputs; i != e; ++i) + Names[i] = outputs[i]; + for (unsigned i = NumOutputs, e = NumExprs; i != e; ++i) + Names[i] = inputs[i]; AsmToks = new (C) Token[NumAsmToks]; for (unsigned i = 0, e = NumAsmToks; i != e; ++i) AsmToks[i] = asmtoks[i]; - LineEnds = new (C) unsigned[NumLineEnds]; - for (unsigned i = 0, e = NumLineEnds; i != e; ++i) - LineEnds[i] = lineends[i]; - Clobbers = new (C) StringRef[NumClobbers]; for (unsigned i = 0, e = NumClobbers; i != e; ++i) { // FIXME: Avoid the allocation/copy if at all possible. diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 2f7cb55c7cc1..c0960ce6a244 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -30,17 +30,15 @@ using namespace clang; namespace { class StmtPrinter : public StmtVisitor<StmtPrinter> { raw_ostream &OS; - ASTContext &Context; unsigned IndentLevel; clang::PrinterHelper* Helper; PrintingPolicy Policy; public: - StmtPrinter(raw_ostream &os, ASTContext &C, PrinterHelper* helper, + StmtPrinter(raw_ostream &os, PrinterHelper* helper, const PrintingPolicy &Policy, unsigned Indentation = 0) - : OS(os), Context(C), IndentLevel(Indentation), Helper(helper), - Policy(Policy) {} + : OS(os), IndentLevel(Indentation), Helper(helper), Policy(Policy) {} void PrintStmt(Stmt *S) { PrintStmt(S, Policy.Indentation); @@ -181,7 +179,7 @@ void StmtPrinter::VisitAttributedStmt(AttributedStmt *Node) { first = false; } // TODO: check this - (*it)->printPretty(OS, Context); + (*it)->printPretty(OS, Policy); } OS << "]] "; PrintStmt(Node->getSubStmt(), 0); @@ -432,7 +430,12 @@ void StmtPrinter::VisitAsmStmt(AsmStmt *Node) { void StmtPrinter::VisitMSAsmStmt(MSAsmStmt *Node) { // FIXME: Implement MS style inline asm statement printer. - Indent() << "asm ()"; + Indent() << "__asm "; + if (Node->hasBraces()) + OS << "{\n"; + OS << *(Node->getAsmString()) << "\n"; + if (Node->hasBraces()) + Indent() << "}\n"; } void StmtPrinter::VisitObjCAtTryStmt(ObjCAtTryStmt *Node) { @@ -1390,7 +1393,7 @@ void StmtPrinter::VisitCXXNewExpr(CXXNewExpr *E) { std::string TypeS; if (Expr *Size = E->getArraySize()) { llvm::raw_string_ostream s(TypeS); - Size->printPretty(s, Context, Helper, Policy); + Size->printPretty(s, Helper, Policy); s.flush(); TypeS = "[" + TypeS + "]"; } @@ -1799,13 +1802,12 @@ void StmtPrinter::VisitAsTypeExpr(AsTypeExpr *Node) { // Stmt method implementations //===----------------------------------------------------------------------===// -void Stmt::dumpPretty(ASTContext& Context) const { - printPretty(llvm::errs(), Context, 0, - PrintingPolicy(Context.getLangOpts())); +void Stmt::dumpPretty(ASTContext &Context) const { + printPretty(llvm::errs(), 0, PrintingPolicy(Context.getLangOpts())); } -void Stmt::printPretty(raw_ostream &OS, ASTContext& Context, - PrinterHelper* Helper, +void Stmt::printPretty(raw_ostream &OS, + PrinterHelper *Helper, const PrintingPolicy &Policy, unsigned Indentation) const { if (this == 0) { @@ -1813,12 +1815,12 @@ void Stmt::printPretty(raw_ostream &OS, ASTContext& Context, return; } - if (Policy.Dump && &Context) { - dump(OS, Context.getSourceManager()); + if (Policy.DumpSourceManager) { + dump(OS, *Policy.DumpSourceManager); return; } - StmtPrinter P(OS, Context, Helper, Policy, Indentation); + StmtPrinter P(OS, Helper, Policy, Indentation); P.Visit(const_cast<Stmt*>(this)); } diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp index f8dd396d92df..95ff4edf1d6b 100644 --- a/lib/AST/TemplateBase.cpp +++ b/lib/AST/TemplateBase.cpp @@ -556,8 +556,7 @@ const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB, const ASTTemplateArgumentListInfo * ASTTemplateArgumentListInfo::Create(ASTContext &C, const TemplateArgumentListInfo &List) { - std::size_t size = sizeof(CXXDependentScopeMemberExpr) + - ASTTemplateArgumentListInfo::sizeFor(List.size()); + std::size_t size = ASTTemplateArgumentListInfo::sizeFor(List.size()); void *Mem = C.Allocate(size, llvm::alignOf<ASTTemplateArgumentListInfo>()); ASTTemplateArgumentListInfo *TAI = new (Mem) ASTTemplateArgumentListInfo(); TAI->initializeFrom(List); @@ -642,6 +641,7 @@ ASTTemplateKWAndArgsInfo::initializeFrom(SourceLocation TemplateKWLoc) { std::size_t ASTTemplateKWAndArgsInfo::sizeFor(unsigned NumTemplateArgs) { // Add space for the template keyword location. + // FIXME: There's room for this in the padding before the template args in + // 64-bit builds. return Base::sizeFor(NumTemplateArgs) + sizeof(SourceLocation); } - diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index e68950200fd0..8065b2d98f32 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -145,6 +145,9 @@ DiagnosticsEngine::GetDiagStatePointForLoc(SourceLocation L) const { assert(DiagStatePoints.front().Loc.isInvalid() && "Should have created a DiagStatePoint for command-line"); + if (!SourceMgr) + return DiagStatePoints.end() - 1; + FullSourceLoc Loc(L, *SourceMgr); if (Loc.isInvalid()) return DiagStatePoints.end() - 1; @@ -167,8 +170,9 @@ void DiagnosticsEngine::setDiagnosticMapping(diag::kind Diag, diag::Mapping Map, (Map == diag::MAP_FATAL || Map == diag::MAP_ERROR)) && "Cannot map errors into warnings!"); assert(!DiagStatePoints.empty()); + assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location"); - FullSourceLoc Loc(L, *SourceMgr); + FullSourceLoc Loc = SourceMgr? FullSourceLoc(L, *SourceMgr) : FullSourceLoc(); FullSourceLoc LastStateChangePos = DiagStatePoints.back().Loc; // Don't allow a mapping to a warning override an error/fatal mapping. if (Map == diag::MAP_WARNING) { diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 883864fd9a18..1d495f1f6412 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -3083,9 +3083,8 @@ public: unsigned &NumAliases) const; virtual bool validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &Info) const { - // FIXME: Check if this is complete switch (*Name) { - default: + default: break; case 'l': // r0-r7 case 'h': // r8-r15 case 'w': // VFP Floating point register single precision diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index 65c782e1cfe7..59ed31361616 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -229,6 +229,35 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, return RValue::get(Result); } + + case Builtin::BI__builtin_conj: + case Builtin::BI__builtin_conjf: + case Builtin::BI__builtin_conjl: { + ComplexPairTy ComplexVal = EmitComplexExpr(E->getArg(0)); + Value *Real = ComplexVal.first; + Value *Imag = ComplexVal.second; + Value *Zero = + Imag->getType()->isFPOrFPVectorTy() + ? llvm::ConstantFP::getZeroValueForNegation(Imag->getType()) + : llvm::Constant::getNullValue(Imag->getType()); + + Imag = Builder.CreateFSub(Zero, Imag, "sub"); + return RValue::getComplex(std::make_pair(Real, Imag)); + } + case Builtin::BI__builtin_creal: + case Builtin::BI__builtin_crealf: + case Builtin::BI__builtin_creall: { + ComplexPairTy ComplexVal = EmitComplexExpr(E->getArg(0)); + return RValue::get(ComplexVal.first); + } + + case Builtin::BI__builtin_cimag: + case Builtin::BI__builtin_cimagf: + case Builtin::BI__builtin_cimagl: { + ComplexPairTy ComplexVal = EmitComplexExpr(E->getArg(0)); + return RValue::get(ComplexVal.second); + } + case Builtin::BI__builtin_ctzs: case Builtin::BI__builtin_ctz: case Builtin::BI__builtin_ctzl: @@ -1720,8 +1749,29 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, Ops.push_back(GetPointeeAlignmentValue(E->getArg(0))); return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vld1, Ty), Ops, "vld1"); - case ARM::BI__builtin_neon_vld1_lane_v: - case ARM::BI__builtin_neon_vld1q_lane_v: { + case ARM::BI__builtin_neon_vld1q_lane_v: + // Handle 64-bit integer elements as a special case. Use shuffles of + // one-element vectors to avoid poor code for i64 in the backend. + if (VTy->getElementType()->isIntegerTy(64)) { + // Extract the other lane. + Ops[1] = Builder.CreateBitCast(Ops[1], Ty); + int Lane = cast<ConstantInt>(Ops[2])->getZExtValue(); + Value *SV = llvm::ConstantVector::get(ConstantInt::get(Int32Ty, 1-Lane)); + Ops[1] = Builder.CreateShuffleVector(Ops[1], Ops[1], SV); + // Load the value as a one-element vector. + Ty = llvm::VectorType::get(VTy->getElementType(), 1); + Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld1, Ty); + Value *Ld = Builder.CreateCall2(F, Ops[0], + GetPointeeAlignmentValue(E->getArg(0))); + // Combine them. + SmallVector<Constant*, 2> Indices; + Indices.push_back(ConstantInt::get(Int32Ty, 1-Lane)); + Indices.push_back(ConstantInt::get(Int32Ty, Lane)); + SV = llvm::ConstantVector::get(Indices); + return Builder.CreateShuffleVector(Ops[1], Ld, SV, "vld1q_lane"); + } + // fall through + case ARM::BI__builtin_neon_vld1_lane_v: { Ops[1] = Builder.CreateBitCast(Ops[1], Ty); Ty = llvm::PointerType::getUnqual(VTy->getElementType()); Ops[0] = Builder.CreateBitCast(Ops[0], Ty); @@ -2086,8 +2136,19 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, Ops.push_back(GetPointeeAlignmentValue(E->getArg(0))); return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst1, Ty), Ops, ""); - case ARM::BI__builtin_neon_vst1_lane_v: - case ARM::BI__builtin_neon_vst1q_lane_v: { + case ARM::BI__builtin_neon_vst1q_lane_v: + // Handle 64-bit integer elements as a special case. Use a shuffle to get + // a one-element vector and avoid poor code for i64 in the backend. + if (VTy->getElementType()->isIntegerTy(64)) { + Ops[1] = Builder.CreateBitCast(Ops[1], Ty); + Value *SV = llvm::ConstantVector::get(cast<llvm::Constant>(Ops[2])); + Ops[1] = Builder.CreateShuffleVector(Ops[1], Ops[1], SV); + Ops[2] = GetPointeeAlignmentValue(E->getArg(0)); + return Builder.CreateCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst1, + Ops[1]->getType()), Ops); + } + // fall through + case ARM::BI__builtin_neon_vst1_lane_v: { Ops[1] = Builder.CreateBitCast(Ops[1], Ty); Ops[1] = Builder.CreateExtractElement(Ops[1], Ops[2]); Ty = llvm::PointerType::getUnqual(Ops[1]->getType()); diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 00127ac72f4d..fd1c7a339807 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -94,8 +94,10 @@ llvm::DIDescriptor CGDebugInfo::getContextDescriptor(const Decl *Context) { llvm::DenseMap<const Decl *, llvm::WeakVH>::iterator I = RegionMap.find(Context); - if (I != RegionMap.end()) - return llvm::DIDescriptor(dyn_cast_or_null<llvm::MDNode>(&*I->second)); + if (I != RegionMap.end()) { + llvm::Value *V = I->second; + return llvm::DIDescriptor(dyn_cast_or_null<llvm::MDNode>(V)); + } // Check namespace. if (const NamespaceDecl *NSDecl = dyn_cast<NamespaceDecl>(Context)) @@ -227,8 +229,8 @@ llvm::DIFile CGDebugInfo::getOrCreateFile(SourceLocation Loc) { if (it != DIFileCache.end()) { // Verify that the information still exists. - if (&*it->second) - return llvm::DIFile(cast<llvm::MDNode>(it->second)); + if (llvm::Value *V = it->second) + return llvm::DIFile(cast<llvm::MDNode>(V)); } llvm::DIFile F = DBuilder.createFile(PLoc.getFilename(), getCurrentDirname()); @@ -525,8 +527,10 @@ llvm::DIDescriptor CGDebugInfo::createContextChain(const Decl *Context) { // See if we already have the parent. llvm::DenseMap<const Decl *, llvm::WeakVH>::iterator I = RegionMap.find(Context); - if (I != RegionMap.end()) - return llvm::DIDescriptor(dyn_cast_or_null<llvm::MDNode>(&*I->second)); + if (I != RegionMap.end()) { + llvm::Value *V = I->second; + return llvm::DIDescriptor(dyn_cast_or_null<llvm::MDNode>(V)); + } // Check namespace. if (const NamespaceDecl *NSDecl = dyn_cast<NamespaceDecl>(Context)) @@ -1660,8 +1664,8 @@ llvm::DIType CGDebugInfo::getTypeOrNull(QualType Ty) { TypeCache.find(Ty.getAsOpaquePtr()); if (it != TypeCache.end()) { // Verify that the debug info still exists. - if (&*it->second) - return llvm::DIType(cast<llvm::MDNode>(it->second)); + if (llvm::Value *V = it->second) + return llvm::DIType(cast<llvm::MDNode>(V)); } return llvm::DIType(); @@ -1679,8 +1683,8 @@ llvm::DIType CGDebugInfo::getCompletedTypeOrNull(QualType Ty) { CompletedTypeCache.find(Ty.getAsOpaquePtr()); if (it != CompletedTypeCache.end()) { // Verify that the debug info still exists. - if (&*it->second) - return llvm::DIType(cast<llvm::MDNode>(it->second)); + if (llvm::Value *V = it->second) + return llvm::DIType(cast<llvm::MDNode>(V)); } return llvm::DIType(); @@ -1942,7 +1946,8 @@ llvm::DISubprogram CGDebugInfo::getFunctionDeclaration(const Decl *D) { llvm::DenseMap<const FunctionDecl *, llvm::WeakVH>::iterator MI = SPCache.find(FD->getCanonicalDecl()); if (MI != SPCache.end()) { - llvm::DISubprogram SP(dyn_cast_or_null<llvm::MDNode>(&*MI->second)); + llvm::Value *V = MI->second; + llvm::DISubprogram SP(dyn_cast_or_null<llvm::MDNode>(V)); if (SP.isSubprogram() && !llvm::DISubprogram(SP).isDefinition()) return SP; } @@ -1953,7 +1958,8 @@ llvm::DISubprogram CGDebugInfo::getFunctionDeclaration(const Decl *D) { llvm::DenseMap<const FunctionDecl *, llvm::WeakVH>::iterator MI = SPCache.find(NextFD->getCanonicalDecl()); if (MI != SPCache.end()) { - llvm::DISubprogram SP(dyn_cast_or_null<llvm::MDNode>(&*MI->second)); + llvm::Value *V = MI->second; + llvm::DISubprogram SP(dyn_cast_or_null<llvm::MDNode>(V)); if (SP.isSubprogram() && !llvm::DISubprogram(SP).isDefinition()) return SP; } @@ -2013,7 +2019,8 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, QualType FnType, llvm::DenseMap<const FunctionDecl *, llvm::WeakVH>::iterator FI = SPCache.find(FD->getCanonicalDecl()); if (FI != SPCache.end()) { - llvm::DIDescriptor SP(dyn_cast_or_null<llvm::MDNode>(&*FI->second)); + llvm::Value *V = FI->second; + llvm::DIDescriptor SP(dyn_cast_or_null<llvm::MDNode>(V)); if (SP.isSubprogram() && llvm::DISubprogram(SP).isDefinition()) { llvm::MDNode *SPN = SP; LexicalBlockStack.push_back(SPN); @@ -2701,15 +2708,15 @@ void CGDebugInfo::finalize(void) { = ReplaceMap.begin(), VE = ReplaceMap.end(); VI != VE; ++VI) { llvm::DIType Ty, RepTy; // Verify that the debug info still exists. - if (&*VI->second) - Ty = llvm::DIType(cast<llvm::MDNode>(VI->second)); + if (llvm::Value *V = VI->second) + Ty = llvm::DIType(cast<llvm::MDNode>(V)); llvm::DenseMap<void *, llvm::WeakVH>::iterator it = TypeCache.find(VI->first); if (it != TypeCache.end()) { // Verify that the debug info still exists. - if (&*it->second) - RepTy = llvm::DIType(cast<llvm::MDNode>(it->second)); + if (llvm::Value *V = it->second) + RepTy = llvm::DIType(cast<llvm::MDNode>(V)); } if (Ty.Verify() && Ty.isForwardDecl() && RepTy.Verify()) { diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index ecee7b4931be..1fe4c18badc6 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -938,6 +938,50 @@ llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) { llvm::Value *CodeGenFunction::EmitLoadOfScalar(llvm::Value *Addr, bool Volatile, unsigned Alignment, QualType Ty, llvm::MDNode *TBAAInfo) { + + // For better performance, handle vector loads differently. + if (Ty->isVectorType()) { + llvm::Value *V; + const llvm::Type *EltTy = + cast<llvm::PointerType>(Addr->getType())->getElementType(); + + const llvm::VectorType *VTy = cast<llvm::VectorType>(EltTy); + + // Handle vectors of size 3, like size 4 for better performance. + if (VTy->getNumElements() == 3) { + + // Bitcast to vec4 type. + llvm::VectorType *vec4Ty = llvm::VectorType::get(VTy->getElementType(), + 4); + llvm::PointerType *ptVec4Ty = + llvm::PointerType::get(vec4Ty, + (cast<llvm::PointerType>( + Addr->getType()))->getAddressSpace()); + llvm::Value *Cast = Builder.CreateBitCast(Addr, ptVec4Ty, + "castToVec4"); + // Now load value. + llvm::Value *LoadVal = Builder.CreateLoad(Cast, Volatile, "loadVec4"); + + // Shuffle vector to get vec3. + llvm::SmallVector<llvm::Constant*, 3> Mask; + Mask.push_back(llvm::ConstantInt::get( + llvm::Type::getInt32Ty(getLLVMContext()), + 0)); + Mask.push_back(llvm::ConstantInt::get( + llvm::Type::getInt32Ty(getLLVMContext()), + 1)); + Mask.push_back(llvm::ConstantInt::get( + llvm::Type::getInt32Ty(getLLVMContext()), + 2)); + + llvm::Value *MaskV = llvm::ConstantVector::get(Mask); + V = Builder.CreateShuffleVector(LoadVal, + llvm::UndefValue::get(vec4Ty), + MaskV, "extractVec"); + return EmitFromMemory(V, Ty); + } + } + llvm::LoadInst *Load = Builder.CreateLoad(Addr); if (Volatile) Load->setVolatile(true); @@ -984,6 +1028,42 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, llvm::Value *Addr, QualType Ty, llvm::MDNode *TBAAInfo, bool isInit) { + + // Handle vectors differently to get better performance. + if (Ty->isVectorType()) { + llvm::Type *SrcTy = Value->getType(); + llvm::VectorType *VecTy = cast<llvm::VectorType>(SrcTy); + // Handle vec3 special. + if (VecTy->getNumElements() == 3) { + llvm::LLVMContext &VMContext = getLLVMContext(); + + // Our source is a vec3, do a shuffle vector to make it a vec4. + llvm::SmallVector<llvm::Constant*, 4> Mask; + Mask.push_back(llvm::ConstantInt::get( + llvm::Type::getInt32Ty(VMContext), + 0)); + Mask.push_back(llvm::ConstantInt::get( + llvm::Type::getInt32Ty(VMContext), + 1)); + Mask.push_back(llvm::ConstantInt::get( + llvm::Type::getInt32Ty(VMContext), + 2)); + Mask.push_back(llvm::UndefValue::get(llvm::Type::getInt32Ty(VMContext))); + + llvm::Value *MaskV = llvm::ConstantVector::get(Mask); + Value = Builder.CreateShuffleVector(Value, + llvm::UndefValue::get(VecTy), + MaskV, "extractVec"); + SrcTy = llvm::VectorType::get(VecTy->getElementType(), 4); + } + llvm::PointerType *DstPtr = cast<llvm::PointerType>(Addr->getType()); + if (DstPtr->getElementType() != SrcTy) { + llvm::Type *MemTy = + llvm::PointerType::get(SrcTy, DstPtr->getAddressSpace()); + Addr = Builder.CreateBitCast(Addr, MemTy, "storetmp"); + } + } + Value = EmitToMemory(Value, Ty); llvm::StoreInst *Store = Builder.CreateStore(Value, Addr, Volatile); diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 7c2c9f1ecb48..31ea1b5448a7 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -123,7 +123,14 @@ static bool canDevirtualizeMemberFunctionCalls(ASTContext &Context, return false; } - + + // We can devirtualize calls on an object accessed by a class member access + // expression, since by C++11 [basic.life]p6 we know that it can't refer to + // a derived class object constructed in the same location. + if (const MemberExpr *ME = dyn_cast<MemberExpr>(Base)) + if (const ValueDecl *VD = dyn_cast<ValueDecl>(ME->getMemberDecl())) + return VD->getType()->isRecordType(); + // We can always devirtualize calls on temporary object expressions. if (isa<CXXConstructExpr>(Base)) return true; diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index 467c77945e60..d78908dee876 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -1691,14 +1691,36 @@ void CodeGenFunction::EmitMSAsmStmt(const MSAsmStmt &S) { std::vector<llvm::Value*> Args; std::vector<llvm::Type *> ArgTypes; + std::string Constraints; + + // Clobbers + for (unsigned i = 0, e = S.getNumClobbers(); i != e; ++i) { + StringRef Clobber = S.getClobber(i); + + if (Clobber != "memory" && Clobber != "cc") + Clobber = Target.getNormalizedGCCRegisterName(Clobber); + + if (i != 0) + Constraints += ','; + + Constraints += "~{"; + Constraints += Clobber; + Constraints += '}'; + } + // Add machine specific clobbers std::string MachineClobbers = Target.getClobbers(); + if (!MachineClobbers.empty()) { + if (!Constraints.empty()) + Constraints += ','; + Constraints += MachineClobbers; + } llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, ArgTypes, false); llvm::InlineAsm *IA = - llvm::InlineAsm::get(FTy, *S.getAsmString(), MachineClobbers, true); + llvm::InlineAsm::get(FTy, *S.getAsmString(), Constraints, true); llvm::CallInst *Result = Builder.CreateCall(IA, Args); Result->addAttribute(~0, llvm::Attribute::NoUnwind); Result->addAttribute(~0, llvm::Attribute::IANSDialect); diff --git a/lib/CodeGen/CGValue.h b/lib/CodeGen/CGValue.h index a46f313f1f7a..c2b8e4da820c 100644 --- a/lib/CodeGen/CGValue.h +++ b/lib/CodeGen/CGValue.h @@ -128,7 +128,7 @@ class LValue { // The alignment to use when accessing this lvalue. (For vector elements, // this is the alignment of the whole vector.) - unsigned short Alignment; + int64_t Alignment; // objective-c's ivar bool Ivar:1; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index b4234cfbc845..ed67f7b9a1e4 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -5723,6 +5723,9 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(Args.MakeArgString(Plugin)); } + if (Args.hasArg(options::OPT_Z_Xlinker__no_demangle)) + CmdArgs.push_back("--no-demangle"); + AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs); if (D.CCCIsCXX && diff --git a/lib/Frontend/ASTConsumers.cpp b/lib/Frontend/ASTConsumers.cpp index bb1a4e664468..0f0d8352046d 100644 --- a/lib/Frontend/ASTConsumers.cpp +++ b/lib/Frontend/ASTConsumers.cpp @@ -89,7 +89,7 @@ namespace { class ASTDeclNodeLister : public ASTConsumer, public RecursiveASTVisitor<ASTDeclNodeLister> { - typedef RecursiveASTVisitor<ASTPrinter> base; + typedef RecursiveASTVisitor<ASTDeclNodeLister> base; public: ASTDeclNodeLister(raw_ostream *Out = NULL) diff --git a/lib/Frontend/CacheTokens.cpp b/lib/Frontend/CacheTokens.cpp index 58a6b8d19e14..3e666132dc8a 100644 --- a/lib/Frontend/CacheTokens.cpp +++ b/lib/Frontend/CacheTokens.cpp @@ -447,7 +447,7 @@ Offset PTHWriter::EmitCachedSpellings() { void PTHWriter::GeneratePTH(const std::string &MainFile) { // Generate the prologue. - Out << "cfe-pth"; + Out << "cfe-pth" << '\0'; Emit32(PTHManager::Version); // Leave 4 words for the prologue. diff --git a/lib/Lex/PTHLexer.cpp b/lib/Lex/PTHLexer.cpp index f104f9670837..67738e907516 100644 --- a/lib/Lex/PTHLexer.cpp +++ b/lib/Lex/PTHLexer.cpp @@ -452,14 +452,14 @@ PTHManager *PTHManager::Create(const std::string &file, const unsigned char *BufEnd = (unsigned char*)File->getBufferEnd(); // Check the prologue of the file. - if ((BufEnd - BufBeg) < (signed)(sizeof("cfe-pth") + 3 + 4) || - memcmp(BufBeg, "cfe-pth", sizeof("cfe-pth") - 1) != 0) { + if ((BufEnd - BufBeg) < (signed)(sizeof("cfe-pth") + 4 + 4) || + memcmp(BufBeg, "cfe-pth", sizeof("cfe-pth")) != 0) { Diags.Report(diag::err_invalid_pth_file) << file; return 0; } // Read the PTH version. - const unsigned char *p = BufBeg + (sizeof("cfe-pth") - 1); + const unsigned char *p = BufBeg + (sizeof("cfe-pth")); unsigned Version = ReadLE32(p); if (Version < PTHManager::Version) { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index b830d9ccfd9f..cb865cc9c2ba 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -68,7 +68,6 @@ static bool isAttributeLateParsed(const IdentifierInfo &II) { .Default(false); } - /// ParseGNUAttributes - Parse a non-empty attributes list. /// /// [GNU] attributes: @@ -193,6 +192,11 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName, ParseThreadSafetyAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc); return; } + // Type safety attributes have their own grammar. + if (AttrName->isStr("type_tag_for_datatype")) { + ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc); + return; + } ConsumeParen(); // ignore the left paren loc for now @@ -866,7 +870,8 @@ void Parser::ParseLexedAttributes(ParsingClass &Class) { void Parser::ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D, bool EnterScope, bool OnDefinition) { for (unsigned i = 0, ni = LAs.size(); i < ni; ++i) { - LAs[i]->addDecl(D); + if (D) + LAs[i]->addDecl(D); ParseLexedAttribute(*LAs[i], EnterScope, OnDefinition); delete LAs[i]; } @@ -1019,6 +1024,70 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName, *EndLoc = T.getCloseLocation(); } +void Parser::ParseTypeTagForDatatypeAttribute(IdentifierInfo &AttrName, + SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, + SourceLocation *EndLoc) { + assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); + + BalancedDelimiterTracker T(*this, tok::l_paren); + T.consumeOpen(); + + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + T.skipToEnd(); + return; + } + IdentifierInfo *ArgumentKind = Tok.getIdentifierInfo(); + SourceLocation ArgumentKindLoc = ConsumeToken(); + + if (Tok.isNot(tok::comma)) { + Diag(Tok, diag::err_expected_comma); + T.skipToEnd(); + return; + } + ConsumeToken(); + + SourceRange MatchingCTypeRange; + TypeResult MatchingCType = ParseTypeName(&MatchingCTypeRange); + if (MatchingCType.isInvalid()) { + T.skipToEnd(); + return; + } + + bool LayoutCompatible = false; + bool MustBeNull = false; + while (Tok.is(tok::comma)) { + ConsumeToken(); + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + T.skipToEnd(); + return; + } + IdentifierInfo *Flag = Tok.getIdentifierInfo(); + if (Flag->isStr("layout_compatible")) + LayoutCompatible = true; + else if (Flag->isStr("must_be_null")) + MustBeNull = true; + else { + Diag(Tok, diag::err_type_safety_unknown_flag) << Flag; + T.skipToEnd(); + return; + } + ConsumeToken(); // consume flag + } + + if (!T.consumeClose()) { + Attrs.addNewTypeTagForDatatype(&AttrName, AttrNameLoc, 0, AttrNameLoc, + ArgumentKind, ArgumentKindLoc, + MatchingCType.release(), LayoutCompatible, + MustBeNull, AttributeList::AS_GNU); + } + + if (EndLoc) + *EndLoc = T.getCloseLocation(); +} + /// DiagnoseProhibitedCXX11Attribute - We have found the opening square brackets /// of a C++11 attribute-specifier in a location where an attribute is not /// permitted. By C++11 [dcl.attr.grammar]p6, this is ill-formed. Diagnose this diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index d2e4309c3f4e..df9b996aa315 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1495,8 +1495,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { StmtResult ForEachStmt; if (ForRange) { - ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, T.getOpenLocation(), - FirstPart.take(), + ForRangeStmt = Actions.ActOnCXXForRangeStmt(ForLoc, FirstPart.take(), ForRangeInit.ColonLoc, ForRangeInit.RangeExpr.get(), T.getCloseLocation()); @@ -1505,7 +1504,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { // Similarly, we need to do the semantic analysis for a for-range // statement immediately in order to close over temporaries correctly. } else if (ForEach) { - ForEachStmt = Actions.ActOnObjCForCollectionStmt(ForLoc, T.getOpenLocation(), + ForEachStmt = Actions.ActOnObjCForCollectionStmt(ForLoc, FirstPart.take(), Collection.take(), T.getCloseLocation()); @@ -1657,112 +1656,98 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { SourceManager &SrcMgr = PP.getSourceManager(); SourceLocation EndLoc = AsmLoc; SmallVector<Token, 4> AsmToks; - SmallVector<unsigned, 4> LineEnds; - do { - bool InBraces = false; - unsigned short savedBraceCount = 0; - bool InAsmComment = false; - FileID FID; - unsigned LineNo = 0; - unsigned NumTokensRead = 0; - SourceLocation LBraceLoc; - - if (Tok.is(tok::l_brace)) { - // Braced inline asm: consume the opening brace. - InBraces = true; - savedBraceCount = BraceCount; - EndLoc = LBraceLoc = ConsumeBrace(); - ++NumTokensRead; - } else { - // Single-line inline asm; compute which line it is on. - std::pair<FileID, unsigned> ExpAsmLoc = - SrcMgr.getDecomposedExpansionLoc(EndLoc); - FID = ExpAsmLoc.first; - LineNo = SrcMgr.getLineNumber(FID, ExpAsmLoc.second); - } - SourceLocation TokLoc = Tok.getLocation(); - do { - // If we hit EOF, we're done, period. - if (Tok.is(tok::eof)) - break; + bool InBraces = false; + unsigned short savedBraceCount = 0; + bool InAsmComment = false; + FileID FID; + unsigned LineNo = 0; + unsigned NumTokensRead = 0; + SourceLocation LBraceLoc; + + if (Tok.is(tok::l_brace)) { + // Braced inline asm: consume the opening brace. + InBraces = true; + savedBraceCount = BraceCount; + EndLoc = LBraceLoc = ConsumeBrace(); + ++NumTokensRead; + } else { + // Single-line inline asm; compute which line it is on. + std::pair<FileID, unsigned> ExpAsmLoc = + SrcMgr.getDecomposedExpansionLoc(EndLoc); + FID = ExpAsmLoc.first; + LineNo = SrcMgr.getLineNumber(FID, ExpAsmLoc.second); + } - // The asm keyword is a statement separator, so multiple asm statements - // are allowed. - if (!InAsmComment && Tok.is(tok::kw_asm)) - break; + SourceLocation TokLoc = Tok.getLocation(); + do { + // If we hit EOF, we're done, period. + if (Tok.is(tok::eof)) + break; - if (!InAsmComment && Tok.is(tok::semi)) { - // A semicolon in an asm is the start of a comment. - InAsmComment = true; - if (InBraces) { - // Compute which line the comment is on. - std::pair<FileID, unsigned> ExpSemiLoc = - SrcMgr.getDecomposedExpansionLoc(TokLoc); - FID = ExpSemiLoc.first; - LineNo = SrcMgr.getLineNumber(FID, ExpSemiLoc.second); - } - } else if (!InBraces || InAsmComment) { - // If end-of-line is significant, check whether this token is on a - // new line. - std::pair<FileID, unsigned> ExpLoc = - SrcMgr.getDecomposedExpansionLoc(TokLoc); - if (ExpLoc.first != FID || - SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second) != LineNo) { - // If this is a single-line __asm, we're done. - if (!InBraces) - break; - // We're no longer in a comment. - InAsmComment = false; - } else if (!InAsmComment && Tok.is(tok::r_brace)) { - // Single-line asm always ends when a closing brace is seen. - // FIXME: This is compatible with Apple gcc's -fasm-blocks; what - // does MSVC do here? - break; - } + if (!InAsmComment && Tok.is(tok::semi)) { + // A semicolon in an asm is the start of a comment. + InAsmComment = true; + if (InBraces) { + // Compute which line the comment is on. + std::pair<FileID, unsigned> ExpSemiLoc = + SrcMgr.getDecomposedExpansionLoc(TokLoc); + FID = ExpSemiLoc.first; + LineNo = SrcMgr.getLineNumber(FID, ExpSemiLoc.second); } - if (!InAsmComment && InBraces && Tok.is(tok::r_brace) && - BraceCount == (savedBraceCount + 1)) { - // Consume the closing brace, and finish - EndLoc = ConsumeBrace(); + } else if (!InBraces || InAsmComment) { + // If end-of-line is significant, check whether this token is on a + // new line. + std::pair<FileID, unsigned> ExpLoc = + SrcMgr.getDecomposedExpansionLoc(TokLoc); + if (ExpLoc.first != FID || + SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second) != LineNo) { + // If this is a single-line __asm, we're done. + if (!InBraces) + break; + // We're no longer in a comment. + InAsmComment = false; + } else if (!InAsmComment && Tok.is(tok::r_brace)) { + // Single-line asm always ends when a closing brace is seen. + // FIXME: This is compatible with Apple gcc's -fasm-blocks; what + // does MSVC do here? break; } - - // Consume the next token; make sure we don't modify the brace count etc. - // if we are in a comment. - EndLoc = TokLoc; - if (InAsmComment) - PP.Lex(Tok); - else { - AsmToks.push_back(Tok); - ConsumeAnyToken(); - } - TokLoc = Tok.getLocation(); - ++NumTokensRead; - } while (1); - - LineEnds.push_back(AsmToks.size()); - - if (InBraces && BraceCount != savedBraceCount) { - // __asm without closing brace (this can happen at EOF). - Diag(Tok, diag::err_expected_rbrace); - Diag(LBraceLoc, diag::note_matching) << "{"; - return StmtError(); - } else if (NumTokensRead == 0) { - // Empty __asm. - Diag(Tok, diag::err_expected_lbrace); - return StmtError(); } - // Multiple adjacent asm's form together into a single asm statement - // in the AST. - if (!Tok.is(tok::kw_asm)) + if (!InAsmComment && InBraces && Tok.is(tok::r_brace) && + BraceCount == (savedBraceCount + 1)) { + // Consume the closing brace, and finish + EndLoc = ConsumeBrace(); break; - EndLoc = ConsumeToken(); + } + + // Consume the next token; make sure we don't modify the brace count etc. + // if we are in a comment. + EndLoc = TokLoc; + if (InAsmComment) + PP.Lex(Tok); + else { + AsmToks.push_back(Tok); + ConsumeAnyToken(); + } + TokLoc = Tok.getLocation(); + ++NumTokensRead; } while (1); + if (InBraces && BraceCount != savedBraceCount) { + // __asm without closing brace (this can happen at EOF). + Diag(Tok, diag::err_expected_rbrace); + Diag(LBraceLoc, diag::note_matching) << "{"; + return StmtError(); + } else if (NumTokensRead == 0) { + // Empty __asm. + Diag(Tok, diag::err_expected_lbrace); + return StmtError(); + } + // FIXME: We should be passing source locations for better diagnostics. - return Actions.ActOnMSAsmStmt(AsmLoc, llvm::makeArrayRef(AsmToks), - llvm::makeArrayRef(LineEnds), EndLoc); + return Actions.ActOnMSAsmStmt(AsmLoc, LBraceLoc, + llvm::makeArrayRef(AsmToks), EndLoc); } /// ParseAsmStatement - Parse a GNU extended asm statement. diff --git a/lib/Rewrite/RewriteModernObjC.cpp b/lib/Rewrite/RewriteModernObjC.cpp index 9f42fcacfab2..dcd003f50160 100644 --- a/lib/Rewrite/RewriteModernObjC.cpp +++ b/lib/Rewrite/RewriteModernObjC.cpp @@ -241,7 +241,7 @@ namespace { // Get the new text. std::string SStr; llvm::raw_string_ostream S(SStr); - New->printPretty(S, *Context, 0, PrintingPolicy(LangOpts)); + New->printPretty(S, 0, PrintingPolicy(LangOpts)); const std::string &Str = S.str(); // If replacement succeeded or warning disabled return with no warning. @@ -2549,8 +2549,7 @@ Stmt *RewriteModernObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { // The pretty printer for StringLiteral handles escape characters properly. std::string prettyBufS; llvm::raw_string_ostream prettyBuf(prettyBufS); - Exp->getString()->printPretty(prettyBuf, *Context, 0, - PrintingPolicy(LangOpts)); + Exp->getString()->printPretty(prettyBuf, 0, PrintingPolicy(LangOpts)); Preamble += prettyBuf.str(); Preamble += ","; Preamble += utostr(Exp->getString()->getByteLength()) + "};\n"; @@ -4341,7 +4340,7 @@ void RewriteModernObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, std::string SStr; llvm::raw_string_ostream constructorExprBuf(SStr); - GlobalConstructionExp->printPretty(constructorExprBuf, *Context, 0, + GlobalConstructionExp->printPretty(constructorExprBuf, 0, PrintingPolicy(LangOpts)); globalBuf += constructorExprBuf.str(); globalBuf += ";\n"; @@ -5610,7 +5609,7 @@ Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { // Get the new text. std::string SStr; llvm::raw_string_ostream Buf(SStr); - Replacement->printPretty(Buf, *Context); + Replacement->printPretty(Buf); const std::string &Str = Buf.str(); printf("CAST = %s\n", &Str[0]); diff --git a/lib/Rewrite/RewriteObjC.cpp b/lib/Rewrite/RewriteObjC.cpp index 425cd77751ca..37c17e63db94 100644 --- a/lib/Rewrite/RewriteObjC.cpp +++ b/lib/Rewrite/RewriteObjC.cpp @@ -227,7 +227,7 @@ namespace { // Get the new text. std::string SStr; llvm::raw_string_ostream S(SStr); - New->printPretty(S, *Context, 0, PrintingPolicy(LangOpts)); + New->printPretty(S, 0, PrintingPolicy(LangOpts)); const std::string &Str = S.str(); // If replacement succeeded or warning disabled return with no warning. @@ -1720,8 +1720,7 @@ Stmt *RewriteObjC::RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S) { CK, syncExpr); std::string syncExprBufS; llvm::raw_string_ostream syncExprBuf(syncExprBufS); - syncExpr->printPretty(syncExprBuf, *Context, 0, - PrintingPolicy(LangOpts)); + syncExpr->printPretty(syncExprBuf, 0, PrintingPolicy(LangOpts)); syncBuf += syncExprBuf.str(); syncBuf += ");"; @@ -2553,8 +2552,7 @@ Stmt *RewriteObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { // The pretty printer for StringLiteral handles escape characters properly. std::string prettyBufS; llvm::raw_string_ostream prettyBuf(prettyBufS); - Exp->getString()->printPretty(prettyBuf, *Context, 0, - PrintingPolicy(LangOpts)); + Exp->getString()->printPretty(prettyBuf, 0, PrintingPolicy(LangOpts)); Preamble += prettyBuf.str(); Preamble += ","; Preamble += utostr(Exp->getString()->getByteLength()) + "};\n"; @@ -4885,7 +4883,7 @@ Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { // Get the new text. std::string SStr; llvm::raw_string_ostream Buf(SStr); - Replacement->printPretty(Buf, *Context); + Replacement->printPretty(Buf); const std::string &Str = Buf.str(); printf("CAST = %s\n", &Str[0]); diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index 0f209fd7d623..7c79879d976c 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -21,6 +21,8 @@ using namespace clang; size_t AttributeList::allocated_size() const { if (IsAvailability) return AttributeFactory::AvailabilityAllocSize; + else if (IsTypeTagForDatatype) + return AttributeFactory::TypeTagForDatatypeAllocSize; return (sizeof(AttributeList) + NumArgs * sizeof(Expr*)); } diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index 819975143dca..d8d51e77ee24 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -1477,6 +1477,21 @@ void Sema::CheckCompatibleReinterpretCast(QualType SrcType, QualType DestType, Diag(Range.getBegin(), DiagID) << SrcType << DestType << Range; } +static void DiagnoseCastOfObjCSEL(Sema &Self, const ExprResult &SrcExpr, + QualType DestType) { + QualType SrcType = SrcExpr.get()->getType(); + if (const PointerType *SrcPtrTy = SrcType->getAs<PointerType>()) + if (SrcPtrTy->isObjCSelType()) { + QualType DT = DestType; + if (isa<PointerType>(DestType)) + DT = DestType->getPointeeType(); + if (!DT.getUnqualifiedType()->isVoidType()) + Self.Diag(SrcExpr.get()->getExprLoc(), + diag::warn_cast_pointer_from_sel) + << SrcType << DestType << SrcExpr.get()->getSourceRange(); + } +} + static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, bool CStyle, const SourceRange &OpRange, @@ -1721,7 +1736,9 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, if (CStyle && DestType->isObjCObjectPointerType()) { return TC_Success; } - + if (CStyle) + DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType); + // Not casting away constness, so the only remaining check is for compatible // pointer categories. @@ -2058,6 +2075,7 @@ void CastOperation::CheckCStyleCast() { return; } } + DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType); Kind = Self.PrepareScalarCast(SrcExpr, DestType); if (SrcExpr.isInvalid()) diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 2594648b56f8..2559f00f71e0 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -513,6 +513,13 @@ void Sema::checkCall(NamedDecl *FDecl, Expr **Args, I = FDecl->specific_attr_begin<NonNullAttr>(), E = FDecl->specific_attr_end<NonNullAttr>(); I != E; ++I) CheckNonNullArguments(*I, Args, Loc); + + // Type safety checking. + for (specific_attr_iterator<ArgumentWithTypeTagAttr> + i = FDecl->specific_attr_begin<ArgumentWithTypeTagAttr>(), + e = FDecl->specific_attr_end<ArgumentWithTypeTagAttr>(); i != e; ++i) { + CheckArgumentWithTypeTag(*i, Args); + } } /// CheckConstructorCall - Check a constructor call for correctness and safety @@ -3170,7 +3177,7 @@ void Sema::CheckStrlcpycatArguments(const CallExpr *Call, SmallString<128> sizeString; llvm::raw_svector_ostream OS(sizeString); OS << "sizeof("; - DstArg->printPretty(OS, Context, 0, getPrintingPolicy()); + DstArg->printPretty(OS, 0, getPrintingPolicy()); OS << ")"; Diag(OriginalSizeArg->getLocStart(), diag::note_strlcpycat_wrong_size) @@ -3267,10 +3274,10 @@ void Sema::CheckStrncatArguments(const CallExpr *CE, SmallString<128> sizeString; llvm::raw_svector_ostream OS(sizeString); OS << "sizeof("; - DstArg->printPretty(OS, Context, 0, getPrintingPolicy()); + DstArg->printPretty(OS, 0, getPrintingPolicy()); OS << ") - "; OS << "strlen("; - DstArg->printPretty(OS, Context, 0, getPrintingPolicy()); + DstArg->printPretty(OS, 0, getPrintingPolicy()); OS << ") - 1"; Diag(SL, diag::note_strncat_wrong_size) @@ -5468,3 +5475,410 @@ void Sema::DiagnoseEmptyLoopBody(const Stmt *S, Diag(NBody->getSemiLoc(), diag::note_empty_body_on_separate_line); } } + +//===--- Layout compatibility ----------------------------------------------// + +namespace { + +bool isLayoutCompatible(ASTContext &C, QualType T1, QualType T2); + +/// \brief Check if two enumeration types are layout-compatible. +bool isLayoutCompatible(ASTContext &C, EnumDecl *ED1, EnumDecl *ED2) { + // C++11 [dcl.enum] p8: + // Two enumeration types are layout-compatible if they have the same + // underlying type. + return ED1->isComplete() && ED2->isComplete() && + C.hasSameType(ED1->getIntegerType(), ED2->getIntegerType()); +} + +/// \brief Check if two fields are layout-compatible. +bool isLayoutCompatible(ASTContext &C, FieldDecl *Field1, FieldDecl *Field2) { + if (!isLayoutCompatible(C, Field1->getType(), Field2->getType())) + return false; + + if (Field1->isBitField() != Field2->isBitField()) + return false; + + if (Field1->isBitField()) { + // Make sure that the bit-fields are the same length. + unsigned Bits1 = Field1->getBitWidthValue(C); + unsigned Bits2 = Field2->getBitWidthValue(C); + + if (Bits1 != Bits2) + return false; + } + + return true; +} + +/// \brief Check if two standard-layout structs are layout-compatible. +/// (C++11 [class.mem] p17) +bool isLayoutCompatibleStruct(ASTContext &C, + RecordDecl *RD1, + RecordDecl *RD2) { + // If both records are C++ classes, check that base classes match. + if (const CXXRecordDecl *D1CXX = dyn_cast<CXXRecordDecl>(RD1)) { + // If one of records is a CXXRecordDecl we are in C++ mode, + // thus the other one is a CXXRecordDecl, too. + const CXXRecordDecl *D2CXX = cast<CXXRecordDecl>(RD2); + // Check number of base classes. + if (D1CXX->getNumBases() != D2CXX->getNumBases()) + return false; + + // Check the base classes. + for (CXXRecordDecl::base_class_const_iterator + Base1 = D1CXX->bases_begin(), + BaseEnd1 = D1CXX->bases_end(), + Base2 = D2CXX->bases_begin(); + Base1 != BaseEnd1; + ++Base1, ++Base2) { + if (!isLayoutCompatible(C, Base1->getType(), Base2->getType())) + return false; + } + } else if (const CXXRecordDecl *D2CXX = dyn_cast<CXXRecordDecl>(RD2)) { + // If only RD2 is a C++ class, it should have zero base classes. + if (D2CXX->getNumBases() > 0) + return false; + } + + // Check the fields. + RecordDecl::field_iterator Field2 = RD2->field_begin(), + Field2End = RD2->field_end(), + Field1 = RD1->field_begin(), + Field1End = RD1->field_end(); + for ( ; Field1 != Field1End && Field2 != Field2End; ++Field1, ++Field2) { + if (!isLayoutCompatible(C, *Field1, *Field2)) + return false; + } + if (Field1 != Field1End || Field2 != Field2End) + return false; + + return true; +} + +/// \brief Check if two standard-layout unions are layout-compatible. +/// (C++11 [class.mem] p18) +bool isLayoutCompatibleUnion(ASTContext &C, + RecordDecl *RD1, + RecordDecl *RD2) { + llvm::SmallPtrSet<FieldDecl *, 8> UnmatchedFields; + for (RecordDecl::field_iterator Field2 = RD2->field_begin(), + Field2End = RD2->field_end(); + Field2 != Field2End; ++Field2) { + UnmatchedFields.insert(*Field2); + } + + for (RecordDecl::field_iterator Field1 = RD1->field_begin(), + Field1End = RD1->field_end(); + Field1 != Field1End; ++Field1) { + llvm::SmallPtrSet<FieldDecl *, 8>::iterator + I = UnmatchedFields.begin(), + E = UnmatchedFields.end(); + + for ( ; I != E; ++I) { + if (isLayoutCompatible(C, *Field1, *I)) { + bool Result = UnmatchedFields.erase(*I); + (void) Result; + assert(Result); + break; + } + } + if (I == E) + return false; + } + + return UnmatchedFields.empty(); +} + +bool isLayoutCompatible(ASTContext &C, RecordDecl *RD1, RecordDecl *RD2) { + if (RD1->isUnion() != RD2->isUnion()) + return false; + + if (RD1->isUnion()) + return isLayoutCompatibleUnion(C, RD1, RD2); + else + return isLayoutCompatibleStruct(C, RD1, RD2); +} + +/// \brief Check if two types are layout-compatible in C++11 sense. +bool isLayoutCompatible(ASTContext &C, QualType T1, QualType T2) { + if (T1.isNull() || T2.isNull()) + return false; + + // C++11 [basic.types] p11: + // If two types T1 and T2 are the same type, then T1 and T2 are + // layout-compatible types. + if (C.hasSameType(T1, T2)) + return true; + + T1 = T1.getCanonicalType().getUnqualifiedType(); + T2 = T2.getCanonicalType().getUnqualifiedType(); + + const Type::TypeClass TC1 = T1->getTypeClass(); + const Type::TypeClass TC2 = T2->getTypeClass(); + + if (TC1 != TC2) + return false; + + if (TC1 == Type::Enum) { + return isLayoutCompatible(C, + cast<EnumType>(T1)->getDecl(), + cast<EnumType>(T2)->getDecl()); + } else if (TC1 == Type::Record) { + if (!T1->isStandardLayoutType() || !T2->isStandardLayoutType()) + return false; + + return isLayoutCompatible(C, + cast<RecordType>(T1)->getDecl(), + cast<RecordType>(T2)->getDecl()); + } + + return false; +} +} + +//===--- CHECK: pointer_with_type_tag attribute: datatypes should match ----// + +namespace { +/// \brief Given a type tag expression find the type tag itself. +/// +/// \param TypeExpr Type tag expression, as it appears in user's code. +/// +/// \param VD Declaration of an identifier that appears in a type tag. +/// +/// \param MagicValue Type tag magic value. +bool FindTypeTagExpr(const Expr *TypeExpr, const ASTContext &Ctx, + const ValueDecl **VD, uint64_t *MagicValue) { + while(true) { + if (!TypeExpr) + return false; + + TypeExpr = TypeExpr->IgnoreParenImpCasts()->IgnoreParenCasts(); + + switch (TypeExpr->getStmtClass()) { + case Stmt::UnaryOperatorClass: { + const UnaryOperator *UO = cast<UnaryOperator>(TypeExpr); + if (UO->getOpcode() == UO_AddrOf || UO->getOpcode() == UO_Deref) { + TypeExpr = UO->getSubExpr(); + continue; + } + return false; + } + + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DRE = cast<DeclRefExpr>(TypeExpr); + *VD = DRE->getDecl(); + return true; + } + + case Stmt::IntegerLiteralClass: { + const IntegerLiteral *IL = cast<IntegerLiteral>(TypeExpr); + llvm::APInt MagicValueAPInt = IL->getValue(); + if (MagicValueAPInt.getActiveBits() <= 64) { + *MagicValue = MagicValueAPInt.getZExtValue(); + return true; + } else + return false; + } + + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: { + const AbstractConditionalOperator *ACO = + cast<AbstractConditionalOperator>(TypeExpr); + bool Result; + if (ACO->getCond()->EvaluateAsBooleanCondition(Result, Ctx)) { + if (Result) + TypeExpr = ACO->getTrueExpr(); + else + TypeExpr = ACO->getFalseExpr(); + continue; + } + return false; + } + + case Stmt::BinaryOperatorClass: { + const BinaryOperator *BO = cast<BinaryOperator>(TypeExpr); + if (BO->getOpcode() == BO_Comma) { + TypeExpr = BO->getRHS(); + continue; + } + return false; + } + + default: + return false; + } + } +} + +/// \brief Retrieve the C type corresponding to type tag TypeExpr. +/// +/// \param TypeExpr Expression that specifies a type tag. +/// +/// \param MagicValues Registered magic values. +/// +/// \param FoundWrongKind Set to true if a type tag was found, but of a wrong +/// kind. +/// +/// \param TypeInfo Information about the corresponding C type. +/// +/// \returns true if the corresponding C type was found. +bool GetMatchingCType( + const IdentifierInfo *ArgumentKind, + const Expr *TypeExpr, const ASTContext &Ctx, + const llvm::DenseMap<Sema::TypeTagMagicValue, + Sema::TypeTagData> *MagicValues, + bool &FoundWrongKind, + Sema::TypeTagData &TypeInfo) { + FoundWrongKind = false; + + // Variable declaration that has type_tag_for_datatype attribute. + const ValueDecl *VD = NULL; + + uint64_t MagicValue; + + if (!FindTypeTagExpr(TypeExpr, Ctx, &VD, &MagicValue)) + return false; + + if (VD) { + for (specific_attr_iterator<TypeTagForDatatypeAttr> + I = VD->specific_attr_begin<TypeTagForDatatypeAttr>(), + E = VD->specific_attr_end<TypeTagForDatatypeAttr>(); + I != E; ++I) { + if (I->getArgumentKind() != ArgumentKind) { + FoundWrongKind = true; + return false; + } + TypeInfo.Type = I->getMatchingCType(); + TypeInfo.LayoutCompatible = I->getLayoutCompatible(); + TypeInfo.MustBeNull = I->getMustBeNull(); + return true; + } + return false; + } + + if (!MagicValues) + return false; + + llvm::DenseMap<Sema::TypeTagMagicValue, + Sema::TypeTagData>::const_iterator I = + MagicValues->find(std::make_pair(ArgumentKind, MagicValue)); + if (I == MagicValues->end()) + return false; + + TypeInfo = I->second; + return true; +} +} // unnamed namespace + +void Sema::RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind, + uint64_t MagicValue, QualType Type, + bool LayoutCompatible, + bool MustBeNull) { + if (!TypeTagForDatatypeMagicValues) + TypeTagForDatatypeMagicValues.reset( + new llvm::DenseMap<TypeTagMagicValue, TypeTagData>); + + TypeTagMagicValue Magic(ArgumentKind, MagicValue); + (*TypeTagForDatatypeMagicValues)[Magic] = + TypeTagData(Type, LayoutCompatible, MustBeNull); +} + +namespace { +bool IsSameCharType(QualType T1, QualType T2) { + const BuiltinType *BT1 = T1->getAs<BuiltinType>(); + if (!BT1) + return false; + + const BuiltinType *BT2 = T2->getAs<BuiltinType>(); + if (!BT2) + return false; + + BuiltinType::Kind T1Kind = BT1->getKind(); + BuiltinType::Kind T2Kind = BT2->getKind(); + + return (T1Kind == BuiltinType::SChar && T2Kind == BuiltinType::Char_S) || + (T1Kind == BuiltinType::UChar && T2Kind == BuiltinType::Char_U) || + (T1Kind == BuiltinType::Char_U && T2Kind == BuiltinType::UChar) || + (T1Kind == BuiltinType::Char_S && T2Kind == BuiltinType::SChar); +} +} // unnamed namespace + +void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr, + const Expr * const *ExprArgs) { + const IdentifierInfo *ArgumentKind = Attr->getArgumentKind(); + bool IsPointerAttr = Attr->getIsPointer(); + + const Expr *TypeTagExpr = ExprArgs[Attr->getTypeTagIdx()]; + bool FoundWrongKind; + TypeTagData TypeInfo; + if (!GetMatchingCType(ArgumentKind, TypeTagExpr, Context, + TypeTagForDatatypeMagicValues.get(), + FoundWrongKind, TypeInfo)) { + if (FoundWrongKind) + Diag(TypeTagExpr->getExprLoc(), + diag::warn_type_tag_for_datatype_wrong_kind) + << TypeTagExpr->getSourceRange(); + return; + } + + const Expr *ArgumentExpr = ExprArgs[Attr->getArgumentIdx()]; + if (IsPointerAttr) { + // Skip implicit cast of pointer to `void *' (as a function argument). + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgumentExpr)) + if (ICE->getType()->isVoidPointerType()) + ArgumentExpr = ICE->getSubExpr(); + } + QualType ArgumentType = ArgumentExpr->getType(); + + // Passing a `void*' pointer shouldn't trigger a warning. + if (IsPointerAttr && ArgumentType->isVoidPointerType()) + return; + + if (TypeInfo.MustBeNull) { + // Type tag with matching void type requires a null pointer. + if (!ArgumentExpr->isNullPointerConstant(Context, + Expr::NPC_ValueDependentIsNotNull)) { + Diag(ArgumentExpr->getExprLoc(), + diag::warn_type_safety_null_pointer_required) + << ArgumentKind->getName() + << ArgumentExpr->getSourceRange() + << TypeTagExpr->getSourceRange(); + } + return; + } + + QualType RequiredType = TypeInfo.Type; + if (IsPointerAttr) + RequiredType = Context.getPointerType(RequiredType); + + bool mismatch = false; + if (!TypeInfo.LayoutCompatible) { + mismatch = !Context.hasSameType(ArgumentType, RequiredType); + + // C++11 [basic.fundamental] p1: + // Plain char, signed char, and unsigned char are three distinct types. + // + // But we treat plain `char' as equivalent to `signed char' or `unsigned + // char' depending on the current char signedness mode. + if (mismatch) + if ((IsPointerAttr && IsSameCharType(ArgumentType->getPointeeType(), + RequiredType->getPointeeType())) || + (!IsPointerAttr && IsSameCharType(ArgumentType, RequiredType))) + mismatch = false; + } else + if (IsPointerAttr) + mismatch = !isLayoutCompatible(Context, + ArgumentType->getPointeeType(), + RequiredType->getPointeeType()); + else + mismatch = !isLayoutCompatible(Context, ArgumentType, RequiredType); + + if (mismatch) + Diag(ArgumentExpr->getExprLoc(), diag::warn_type_safety_type_mismatch) + << ArgumentType << ArgumentKind->getName() + << TypeInfo.LayoutCompatible << RequiredType + << ArgumentExpr->getSourceRange() + << TypeTagExpr->getSourceRange(); +} + diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 9fa757d50a29..adf132715738 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -4476,7 +4476,6 @@ static void AddObjCExpressionResults(ResultBuilder &Results, bool NeedAt) { Builder.AddResultTypeChunk("NSDictionary *"); Builder.AddTypedTextChunk(OBJC_AT_KEYWORD_NAME(NeedAt,"{")); Builder.AddPlaceholderChunk("key"); - Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddChunk(CodeCompletionString::CK_Colon); Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); Builder.AddPlaceholderChunk("object, ..."); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 3aae99ab74e2..ea181de00a98 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -6174,6 +6174,7 @@ namespace { Decl *OrigDecl; bool isRecordType; bool isPODType; + bool isReferenceType; public: typedef EvaluatedExprVisitor<SelfReferenceChecker> Inherited; @@ -6182,9 +6183,11 @@ namespace { S(S), OrigDecl(OrigDecl) { isPODType = false; isRecordType = false; + isReferenceType = false; if (ValueDecl *VD = dyn_cast<ValueDecl>(OrigDecl)) { isPODType = VD->getType().isPODType(S.Context); isRecordType = VD->getType()->isRecordType(); + isReferenceType = VD->getType()->isReferenceType(); } } @@ -6192,9 +6195,9 @@ namespace { // to determine which DeclRefExpr's to check. Assume that the casts // are present and continue visiting the expression. void HandleExpr(Expr *E) { - // Skip checking T a = a where T is not a record type. Doing so is a - // way to silence uninitialized warnings. - if (isRecordType) + // Skip checking T a = a where T is not a record or reference type. + // Doing so is a way to silence uninitialized warnings. + if (isRecordType || isReferenceType) if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) HandleDeclRefExpr(DRE); @@ -6309,11 +6312,11 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, } // Check for self-references within variable initializers. - // Variables declared within a function/method body are handled - // by a dataflow analysis. + // Variables declared within a function/method body (except for references) + // are handled by a dataflow analysis. // Record types initialized by initializer list are handled here. // Initialization by constructors are handled in TryConstructorInitialization. - if (!VDecl->hasLocalStorage() && !VDecl->isStaticLocal() && + if ((!VDecl->hasLocalStorage() || VDecl->getType()->isReferenceType()) && (isa<InitListExpr>(Init) || !VDecl->getType()->isRecordType())) CheckSelfReference(RealDecl, Init); @@ -6754,6 +6757,10 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl, diag::err_abstract_type_in_decl, AbstractVariableType)) Var->setInvalidDecl(); + if (!Type->isDependentType() && !Var->isInvalidDecl() && + Var->getStorageClass() == SC_PrivateExtern) + Diag(Var->getLocation(), diag::warn_private_extern); + return; case VarDecl::TentativeDefinition: @@ -7027,6 +7034,42 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) { // Note that we are no longer parsing the initializer for this declaration. ParsingInitForAutoVars.erase(ThisDecl); + + // Now we have parsed the initializer and can update the table of magic + // tag values. + if (ThisDecl && ThisDecl->hasAttr<TypeTagForDatatypeAttr>()) { + const VarDecl *VD = dyn_cast<VarDecl>(ThisDecl); + if (VD && VD->getType()->isIntegralOrEnumerationType()) { + for (specific_attr_iterator<TypeTagForDatatypeAttr> + I = ThisDecl->specific_attr_begin<TypeTagForDatatypeAttr>(), + E = ThisDecl->specific_attr_end<TypeTagForDatatypeAttr>(); + I != E; ++I) { + const Expr *MagicValueExpr = VD->getInit(); + if (!MagicValueExpr) { + continue; + } + llvm::APSInt MagicValueInt; + if (!MagicValueExpr->isIntegerConstantExpr(MagicValueInt, Context)) { + Diag(I->getRange().getBegin(), + diag::err_type_tag_for_datatype_not_ice) + << LangOpts.CPlusPlus << MagicValueExpr->getSourceRange(); + continue; + } + if (MagicValueInt.getActiveBits() > 64) { + Diag(I->getRange().getBegin(), + diag::err_type_tag_for_datatype_too_large) + << LangOpts.CPlusPlus << MagicValueExpr->getSourceRange(); + continue; + } + uint64_t MagicValue = MagicValueInt.getZExtValue(); + RegisterTypeTagForDatatype(I->getArgumentKind(), + MagicValue, + I->getMatchingCType(), + I->getLayoutCompatible(), + I->getMustBeNull()); + } + } + } } Sema::DeclGroupPtrTy @@ -7623,7 +7666,9 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D) { << FD->getName() << "dllimport"; } } - ActOnDocumentableDecl(FD); + // We want to attach documentation to original Decl (which might be + // a function template). + ActOnDocumentableDecl(D); return FD; } @@ -7750,7 +7795,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // Verify that gotos and switch cases don't jump into scopes illegally. if (getCurFunction()->NeedsScopeChecking() && !dcl->isInvalidDecl() && - !hasAnyUnrecoverableErrorsInThisFunction()) + !hasAnyUnrecoverableErrorsInThisFunction() && + !PP.isCodeCompletionEnabled()) DiagnoseInvalidJumps(Body); if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(dcl)) { @@ -9907,6 +9953,13 @@ void Sema::ActOnFields(Scope* S, } } } + if (isa<ObjCContainerDecl>(EnclosingDecl) && + RequireNonAbstractType(FD->getLocation(), FD->getType(), + diag::err_abstract_type_in_decl, + AbstractIvarType)) { + // Ivars can not have abstract class types + FD->setInvalidDecl(); + } if (Record && FDTTy->getDecl()->hasObjectMember()) Record->setHasObjectMember(true); } else if (FDTy->isObjCObjectType()) { @@ -9915,8 +9968,7 @@ void Sema::ActOnFields(Scope* S, << FixItHint::CreateInsertion(FD->getLocation(), "*"); QualType T = Context.getObjCObjectPointerType(FD->getType()); FD->setType(T); - } - else if (!getLangOpts().CPlusPlus) { + } else if (!getLangOpts().CPlusPlus) { if (getLangOpts().ObjCAutoRefCount && Record && !ARCErrReported) { // It's an error in ARC if a field has lifetime. // We don't want to report this in a system header, though, diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 22bff863c5c2..caa7b2f65a9d 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -221,6 +221,53 @@ static bool checkAttributeAtLeastNumArgs(Sema &S, const AttributeList &Attr, return true; } +/// \brief Check if IdxExpr is a valid argument index for a function or +/// instance method D. May output an error. +/// +/// \returns true if IdxExpr is a valid index. +static bool checkFunctionOrMethodArgumentIndex(Sema &S, const Decl *D, + StringRef AttrName, + SourceLocation AttrLoc, + unsigned AttrArgNum, + const Expr *IdxExpr, + uint64_t &Idx) +{ + assert(isFunctionOrMethod(D) && hasFunctionProto(D)); + + // In C++ the implicit 'this' function parameter also counts. + // Parameters are counted from one. + const bool HasImplicitThisParam = isInstanceMethod(D); + const unsigned NumArgs = getFunctionOrMethodNumArgs(D) + HasImplicitThisParam; + const unsigned FirstIdx = 1; + + llvm::APSInt IdxInt; + if (IdxExpr->isTypeDependent() || IdxExpr->isValueDependent() || + !IdxExpr->isIntegerConstantExpr(IdxInt, S.Context)) { + S.Diag(AttrLoc, diag::err_attribute_argument_n_not_int) + << AttrName << AttrArgNum << IdxExpr->getSourceRange(); + return false; + } + + Idx = IdxInt.getLimitedValue(); + if (Idx < FirstIdx || (!isFunctionOrMethodVariadic(D) && Idx > NumArgs)) { + S.Diag(AttrLoc, diag::err_attribute_argument_out_of_bounds) + << AttrName << AttrArgNum << IdxExpr->getSourceRange(); + return false; + } + Idx--; // Convert to zero-based. + if (HasImplicitThisParam) { + if (Idx == 0) { + S.Diag(AttrLoc, + diag::err_attribute_invalid_implicit_this_argument) + << AttrName << IdxExpr->getSourceRange(); + return false; + } + --Idx; + } + + return true; +} + /// /// \brief Check if passed in Decl is a field or potentially shared global var /// \return true if the Decl is a field or potentially shared global variable @@ -3523,25 +3570,16 @@ static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) { D->addAttr(::new (S.Context) PascalAttr(Attr.getRange(), S.Context)); return; case AttributeList::AT_Pcs: { - Expr *Arg = Attr.getArg(0); - StringLiteral *Str = dyn_cast<StringLiteral>(Arg); - if (!Str || !Str->isAscii()) { - S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_string) - << "pcs" << 1; - Attr.setInvalid(); - return; - } - - StringRef StrRef = Str->getString(); PcsAttr::PCSType PCS; - if (StrRef == "aapcs") + switch (CC) { + case CC_AAPCS: PCS = PcsAttr::AAPCS; - else if (StrRef == "aapcs-vfp") + break; + case CC_AAPCS_VFP: PCS = PcsAttr::AAPCS_VFP; - else { - S.Diag(Attr.getLoc(), diag::err_invalid_pcs); - Attr.setInvalid(); - return; + break; + default: + llvm_unreachable("unexpected calling convention in pcs attribute"); } D->addAttr(::new (S.Context) PcsAttr(Attr.getRange(), S.Context, PCS)); @@ -3560,10 +3598,9 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) { if (attr.isInvalid()) return true; - if ((attr.getNumArgs() != 0 && - !(attr.getKind() == AttributeList::AT_Pcs && attr.getNumArgs() == 1)) || - attr.getParameterName()) { - Diag(attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + unsigned ReqArgs = attr.getKind() == AttributeList::AT_Pcs ? 1 : 0; + if (attr.getNumArgs() != ReqArgs || attr.getParameterName()) { + Diag(attr.getLoc(), diag::err_attribute_wrong_number_arguments) << ReqArgs; attr.setInvalid(); return true; } @@ -3594,7 +3631,10 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) { CC = CC_AAPCS_VFP; break; } - // FALLS THROUGH + + attr.setInvalid(); + Diag(attr.getLoc(), diag::err_invalid_pcs); + return true; } default: llvm_unreachable("unexpected attribute kind"); } @@ -3703,6 +3743,79 @@ static void handleLaunchBoundsAttr(Sema &S, Decl *D, const AttributeList &Attr){ } } +static void handleArgumentWithTypeTagAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + StringRef AttrName = Attr.getName()->getName(); + if (!Attr.getParameterName()) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_identifier) + << Attr.getName() << /* arg num = */ 1; + return; + } + + if (Attr.getNumArgs() != 2) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) + << /* required args = */ 3; + return; + } + + IdentifierInfo *ArgumentKind = Attr.getParameterName(); + + if (!isFunctionOrMethod(D) || !hasFunctionProto(D)) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; + return; + } + + uint64_t ArgumentIdx; + if (!checkFunctionOrMethodArgumentIndex(S, D, AttrName, + Attr.getLoc(), 2, + Attr.getArg(0), ArgumentIdx)) + return; + + uint64_t TypeTagIdx; + if (!checkFunctionOrMethodArgumentIndex(S, D, AttrName, + Attr.getLoc(), 3, + Attr.getArg(1), TypeTagIdx)) + return; + + bool IsPointer = (AttrName == "pointer_with_type_tag"); + if (IsPointer) { + // Ensure that buffer has a pointer type. + QualType BufferTy = getFunctionOrMethodArgType(D, ArgumentIdx); + if (!BufferTy->isPointerType()) { + S.Diag(Attr.getLoc(), diag::err_attribute_pointers_only) + << AttrName; + } + } + + D->addAttr(::new (S.Context) ArgumentWithTypeTagAttr(Attr.getRange(), + S.Context, + ArgumentKind, + ArgumentIdx, + TypeTagIdx, + IsPointer)); +} + +static void handleTypeTagForDatatypeAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + IdentifierInfo *PointerKind = Attr.getParameterName(); + if (!PointerKind) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_identifier) + << "type_tag_for_datatype" << 1; + return; + } + + QualType MatchingCType = S.GetTypeFromParser(Attr.getMatchingCType(), NULL); + + D->addAttr(::new (S.Context) TypeTagForDatatypeAttr( + Attr.getRange(), + S.Context, + PointerKind, + MatchingCType, + Attr.getLayoutCompatible(), + Attr.getMustBeNull())); +} + //===----------------------------------------------------------------------===// // Checker-specific attribute handlers. //===----------------------------------------------------------------------===// @@ -4333,6 +4446,14 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, handleAcquiredAfterAttr(S, D, Attr); break; + // Type safety attributes. + case AttributeList::AT_ArgumentWithTypeTag: + handleArgumentWithTypeTagAttr(S, D, Attr); + break; + case AttributeList::AT_TypeTagForDatatype: + handleTypeTagForDatatypeAttr(S, D, Attr); + break; + default: // Ask target about the attribute. const TargetAttributesSema &TargetAttrs = S.getTargetAttributesSema(); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 1d45a6823da1..eeac9b8e8d70 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -9807,7 +9807,7 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, if (!Failed && !Cond) { llvm::SmallString<256> MsgBuffer; llvm::raw_svector_ostream Msg(MsgBuffer); - AssertMessage->printPretty(Msg, Context, 0, getPrintingPolicy()); + AssertMessage->printPretty(Msg, 0, getPrintingPolicy()); Diag(StaticAssertLoc, diag::err_static_assert_failed) << Msg.str() << AssertExpr->getSourceRange(); Failed = true; diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp index 63bfa9dc2d9f..e6266fb0863a 100644 --- a/lib/Sema/SemaExceptionSpec.cpp +++ b/lib/Sema/SemaExceptionSpec.cpp @@ -242,8 +242,7 @@ bool Sema::CheckEquivalentExceptionSpec(FunctionDecl *Old, FunctionDecl *New) { case EST_ComputedNoexcept: OS << "noexcept("; - OldProto->getNoexceptExpr()->printPretty(OS, Context, 0, - getPrintingPolicy()); + OldProto->getNoexceptExpr()->printPretty(OS, 0, getPrintingPolicy()); OS << ")"; break; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 6a503ee2d9b5..3875ba171366 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9461,7 +9461,8 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, // If needed, diagnose invalid gotos and switches in the block. if (getCurFunction()->NeedsScopeChecking() && - !hasAnyUnrecoverableErrorsInThisFunction()) + !hasAnyUnrecoverableErrorsInThisFunction() && + !PP.isCodeCompletionEnabled()) DiagnoseInvalidJumps(cast<CompoundStmt>(Body)); BSI->TheDecl->setBody(cast<CompoundStmt>(Body)); diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index 53f22f65ddf5..8f445e28648d 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -1137,7 +1137,7 @@ Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, goto fail; // There's an implicit 'isa' ivar on all objects. // But we only actually find it this way on objects of type 'id', - // apparently.ghjg + // apparently. if (OTy->isObjCId() && Member->isStr("isa")) { Diag(MemberLoc, diag::warn_objc_isa_use); return Owned(new (Context) ObjCIsaExpr(BaseExpr.take(), IsArrow, MemberLoc, diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index a8744899d70c..9382f7dddc6c 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -57,7 +57,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, StandardConversionSequence &SCS, bool CStyle, bool AllowObjCWritebackConversion); - + static bool IsTransparentUnionStandardConversion(Sema &S, Expr* From, QualType &ToType, bool InOverloadResolution, diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index d22deb2f7f21..86884b787339 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -28,6 +28,7 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" @@ -35,11 +36,15 @@ #include "llvm/ADT/Triple.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetAsmParser.h" +#include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCAsmParser.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" @@ -1516,7 +1521,6 @@ Sema::CheckObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) { StmtResult Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, - SourceLocation LParenLoc, Stmt *First, Expr *collection, SourceLocation RParenLoc) { @@ -1698,9 +1702,9 @@ static bool ObjCEnumerationCollection(Expr *Collection) { && Collection->getType()->getAs<ObjCObjectPointerType>() != 0; } -/// ActOnCXXForRangeStmt - Check and build a C++0x for-range statement. +/// ActOnCXXForRangeStmt - Check and build a C++11 for-range statement. /// -/// C++0x [stmt.ranged]: +/// C++11 [stmt.ranged]: /// A range-based for statement is equivalent to /// /// { @@ -1717,15 +1721,14 @@ static bool ObjCEnumerationCollection(Expr *Collection) { /// The body of the loop is not available yet, since it cannot be analysed until /// we have determined the type of the for-range-declaration. StmtResult -Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, SourceLocation LParenLoc, +Sema::ActOnCXXForRangeStmt(SourceLocation ForLoc, Stmt *First, SourceLocation ColonLoc, Expr *Range, SourceLocation RParenLoc) { if (!First || !Range) return StmtError(); if (ObjCEnumerationCollection(Range)) - return ActOnObjCForCollectionStmt(ForLoc, LParenLoc, First, Range, - RParenLoc); + return ActOnObjCForCollectionStmt(ForLoc, First, Range, RParenLoc); DeclStmt *DS = dyn_cast<DeclStmt>(First); assert(DS && "first part of for range not a decl stmt"); @@ -2759,165 +2762,225 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple, return Owned(NS); } -// needSpaceAsmToken - This function handles whitespace around asm punctuation. -// Returns true if a space should be emitted. -static inline bool needSpaceAsmToken(Token currTok) { - static Token prevTok; - - // No need for space after prevToken. - switch(prevTok.getKind()) { - default: - break; - case tok::l_square: - case tok::r_square: - case tok::l_brace: - case tok::r_brace: - case tok::colon: - prevTok = currTok; - return false; - } +// isMSAsmKeyword - Return true if this is an MS-style inline asm keyword. These +// require special handling. +static bool isMSAsmKeyword(StringRef Name) { + bool Ret = llvm::StringSwitch<bool>(Name) + .Cases("EVEN", "ALIGN", true) // Alignment directives. + .Cases("LENGTH", "SIZE", "TYPE", true) // Type and variable sizes. + .Case("_emit", true) // _emit Pseudoinstruction. + .Default(false); + return Ret; +} - // No need for a space before currToken. - switch(currTok.getKind()) { - default: - break; - case tok::l_square: - case tok::r_square: - case tok::l_brace: - case tok::r_brace: - case tok::comma: - case tok::colon: - prevTok = currTok; - return false; - } - prevTok = currTok; - return true; +static StringRef getSpelling(Sema &SemaRef, Token AsmTok) { + StringRef Asm; + SmallString<512> TokenBuf; + TokenBuf.resize(512); + bool StringInvalid = false; + Asm = SemaRef.PP.getSpelling(AsmTok, TokenBuf, &StringInvalid); + assert (!StringInvalid && "Expected valid string!"); + return Asm; } static void patchMSAsmStrings(Sema &SemaRef, bool &IsSimple, SourceLocation AsmLoc, ArrayRef<Token> AsmToks, - ArrayRef<unsigned> LineEnds, const TargetInfo &TI, + std::vector<llvm::BitVector> &AsmRegs, + std::vector<llvm::BitVector> &AsmNames, std::vector<std::string> &AsmStrings) { assert (!AsmToks.empty() && "Didn't expect an empty AsmToks!"); - // Assume simple asm stmt until we parse a non-register identifer. + // Assume simple asm stmt until we parse a non-register identifer (or we just + // need to bail gracefully). IsSimple = true; - for (unsigned i = 0, e = LineEnds.size(); i != e; ++i) { - SmallString<512> Asm; + SmallString<512> Asm; + unsigned NumAsmStrings = 0; + for (unsigned i = 0, e = AsmToks.size(); i != e; ++i) { + + // Determine if this should be considered a new asm. + bool isNewAsm = i == 0 || AsmToks[i].isAtStartOfLine() || + AsmToks[i].is(tok::kw_asm); + + // Emit the previous asm string. + if (i && isNewAsm) { + AsmStrings[NumAsmStrings++] = Asm.c_str(); + if (AsmToks[i].is(tok::kw_asm)) { + ++i; // Skip __asm + assert (i != e && "Expected another token."); + } + } - // Check the operands. - for (unsigned j = (i == 0) ? 0 : LineEnds[i-1], e = LineEnds[i]; j != e; ++j) { + // Start a new asm string with the opcode. + if (isNewAsm) { + AsmRegs[NumAsmStrings].resize(AsmToks.size()); + AsmNames[NumAsmStrings].resize(AsmToks.size()); - IdentifierInfo *II; - if (j == 0 || (i > 0 && j == LineEnds[i-1])) { - II = AsmToks[j].getIdentifierInfo(); - Asm = II->getName().str(); - continue; + StringRef Piece = AsmToks[i].getIdentifierInfo()->getName(); + // MS-style inline asm keywords require special handling. + if (isMSAsmKeyword(Piece)) + IsSimple = false; + + // TODO: Verify this is a valid opcode. + Asm = Piece; + continue; + } + + if (i && AsmToks[i].hasLeadingSpace()) + Asm += ' '; + + // Check the operand(s). + switch (AsmToks[i].getKind()) { + default: + IsSimple = false; + Asm += getSpelling(SemaRef, AsmToks[i]); + break; + case tok::comma: Asm += ","; break; + case tok::colon: Asm += ":"; break; + case tok::l_square: Asm += "["; break; + case tok::r_square: Asm += "]"; break; + case tok::l_brace: Asm += "{"; break; + case tok::r_brace: Asm += "}"; break; + case tok::numeric_constant: + Asm += getSpelling(SemaRef, AsmToks[i]); + break; + case tok::identifier: { + IdentifierInfo *II = AsmToks[i].getIdentifierInfo(); + StringRef Name = II->getName(); + + // Valid register? + if (TI.isValidGCCRegisterName(Name)) { + AsmRegs[NumAsmStrings].set(i); + Asm += Name; + break; } - if (needSpaceAsmToken(AsmToks[j])) - Asm += " "; + IsSimple = false; - switch (AsmToks[j].getKind()) { - default: - //llvm_unreachable("Unknown token."); + // MS-style inline asm keywords require special handling. + if (isMSAsmKeyword(Name)) { + IsSimple = false; + Asm += Name; break; - case tok::comma: Asm += ","; break; - case tok::colon: Asm += ":"; break; - case tok::l_square: Asm += "["; break; - case tok::r_square: Asm += "]"; break; - case tok::l_brace: Asm += "{"; break; - case tok::r_brace: Asm += "}"; break; - case tok::numeric_constant: { - SmallString<32> TokenBuf; - TokenBuf.resize(32); - bool StringInvalid = false; - Asm += SemaRef.PP.getSpelling(AsmToks[j], TokenBuf, &StringInvalid); - assert (!StringInvalid && "Expected valid string!"); + } + + // FIXME: Why are we missing this segment register? + if (Name == "fs") { + Asm += Name; break; } - case tok::identifier: { - II = AsmToks[j].getIdentifierInfo(); - StringRef Name = II->getName(); - // Valid registers don't need modification. - if (TI.isValidGCCRegisterName(Name)) { - Asm += Name; - break; - } + // Lookup the identifier. + // TODO: Someone with more experience with clang should verify this the + // proper way of doing a symbol lookup. + DeclarationName DeclName(II); + Scope *CurScope = SemaRef.getCurScope(); + LookupResult R(SemaRef, DeclName, AsmLoc, Sema::LookupOrdinaryName); + if (!SemaRef.LookupName(R, CurScope, false/*AllowBuiltinCreation*/)) + break; - // TODO: Lookup the identifier. - IsSimple = false; + assert (R.isSingleResult() && "Expected a single result?!"); + NamedDecl *Decl = R.getFoundDecl(); + switch (Decl->getKind()) { + default: + assert(0 && "Unknown decl kind."); + break; + case Decl::Var: { + case Decl::ParmVar: + AsmNames[NumAsmStrings].set(i); + + VarDecl *Var = cast<VarDecl>(Decl); + QualType Ty = Var->getType(); + (void)Ty; // Avoid warning. + // TODO: Patch identifier with valid operand. One potential idea is to + // probe the backend with type information to guess the possible + // operand. + break; + } } - } // AsmToks[i].getKind() + break; + } } - AsmStrings[i] = Asm.c_str(); } + + // Emit the final (and possibly only) asm string. + AsmStrings[NumAsmStrings] = Asm.c_str(); } // Build the unmodified MSAsmString. static std::string buildMSAsmString(Sema &SemaRef, ArrayRef<Token> AsmToks, - ArrayRef<unsigned> LineEnds) { + unsigned &NumAsmStrings) { assert (!AsmToks.empty() && "Didn't expect an empty AsmToks!"); + NumAsmStrings = 0; + SmallString<512> Asm; - SmallString<512> TokenBuf; - TokenBuf.resize(512); - unsigned AsmLineNum = 0; for (unsigned i = 0, e = AsmToks.size(); i < e; ++i) { - const char *ThisTokBuf = &TokenBuf[0]; - bool StringInvalid = false; - unsigned ThisTokLen = - Lexer::getSpelling(AsmToks[i], ThisTokBuf, SemaRef.getSourceManager(), - SemaRef.getLangOpts(), &StringInvalid); - if (i && (!AsmLineNum || i != LineEnds[AsmLineNum-1]) && - needSpaceAsmToken(AsmToks[i])) - Asm += ' '; - Asm += StringRef(ThisTokBuf, ThisTokLen); - if (i + 1 == LineEnds[AsmLineNum] && i + 1 != AsmToks.size()) { - Asm += '\n'; - ++AsmLineNum; + bool isNewAsm = i == 0 || AsmToks[i].isAtStartOfLine() || + AsmToks[i].is(tok::kw_asm); + + if (isNewAsm) { + ++NumAsmStrings; + if (i) + Asm += '\n'; + if (AsmToks[i].is(tok::kw_asm)) { + i++; // Skip __asm + assert (i != e && "Expected another token"); + } } + + if (i && AsmToks[i].hasLeadingSpace() && !isNewAsm) + Asm += ' '; + + Asm += getSpelling(SemaRef, AsmToks[i]); } return Asm.c_str(); } StmtResult Sema::ActOnMSAsmStmt(SourceLocation AsmLoc, + SourceLocation LBraceLoc, ArrayRef<Token> AsmToks, - ArrayRef<unsigned> LineEnds, SourceLocation EndLoc) { // MS-style inline assembly is not fully supported, so emit a warning. Diag(AsmLoc, diag::warn_unsupported_msasm); SmallVector<StringRef,4> Clobbers; + std::set<std::string> ClobberRegs; + SmallVector<IdentifierInfo*, 4> Inputs; + SmallVector<IdentifierInfo*, 4> Outputs; // Empty asm statements don't need to instantiate the AsmParser, etc. if (AsmToks.empty()) { StringRef AsmString; MSAsmStmt *NS = - new (Context) MSAsmStmt(Context, AsmLoc, /* IsSimple */ true, - /* IsVolatile */ true, AsmToks, LineEnds, + new (Context) MSAsmStmt(Context, AsmLoc, LBraceLoc, /*IsSimple*/ true, + /*IsVolatile*/ true, AsmToks, Inputs, Outputs, AsmString, Clobbers, EndLoc); return Owned(NS); } - std::string AsmString = buildMSAsmString(*this, AsmToks, LineEnds); + unsigned NumAsmStrings; + std::string AsmString = buildMSAsmString(*this, AsmToks, NumAsmStrings); bool IsSimple; + std::vector<llvm::BitVector> Regs; + std::vector<llvm::BitVector> Names; std::vector<std::string> PatchedAsmStrings; - PatchedAsmStrings.resize(LineEnds.size()); + + Regs.resize(NumAsmStrings); + Names.resize(NumAsmStrings); + PatchedAsmStrings.resize(NumAsmStrings); // Rewrite operands to appease the AsmParser. - patchMSAsmStrings(*this, IsSimple, AsmLoc, AsmToks, LineEnds, - Context.getTargetInfo(), PatchedAsmStrings); + patchMSAsmStrings(*this, IsSimple, AsmLoc, AsmToks, + Context.getTargetInfo(), Regs, Names, PatchedAsmStrings); // patchMSAsmStrings doesn't correctly patch non-simple asm statements. if (!IsSimple) { MSAsmStmt *NS = - new (Context) MSAsmStmt(Context, AsmLoc, /* IsSimple */ true, - /* IsVolatile */ true, AsmToks, LineEnds, + new (Context) MSAsmStmt(Context, AsmLoc, LBraceLoc, /*IsSimple*/ true, + /*IsVolatile*/ true, AsmToks, Inputs, Outputs, AsmString, Clobbers, EndLoc); return Owned(NS); } @@ -2947,7 +3010,7 @@ StmtResult Sema::ActOnMSAsmStmt(SourceLocation AsmLoc, // Tell SrcMgr about this buffer, which is what the parser will pick up. SrcMgr.AddNewSourceBuffer(Buffer, llvm::SMLoc()); - OwningPtr<llvm::MCStreamer> Str; + OwningPtr<llvm::MCStreamer> Str(createNullStreamer(Ctx)); OwningPtr<llvm::MCAsmParser> Parser(createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI)); OwningPtr<llvm::MCTargetAsmParser> @@ -2956,13 +3019,63 @@ StmtResult Sema::ActOnMSAsmStmt(SourceLocation AsmLoc, Parser->setAssemblerDialect(1); Parser->setTargetParser(*TargetParser.get()); - // TODO: Start parsing. + // Prime the lexer. + Parser->Lex(); + + // Parse the opcode. + StringRef IDVal; + Parser->ParseIdentifier(IDVal); + + // Canonicalize the opcode to lower case. + SmallString<128> Opcode; + for (unsigned i = 0, e = IDVal.size(); i != e; ++i) + Opcode.push_back(tolower(IDVal[i])); + + // Parse the operands. + llvm::SMLoc IDLoc; + SmallVector<llvm::MCParsedAsmOperand*, 8> Operands; + bool HadError = TargetParser->ParseInstruction(Opcode.str(), IDLoc, + Operands); + assert (!HadError && "Unexpected error parsing instruction"); + + // Match the MCInstr. + SmallVector<llvm::MCInst, 2> Instrs; + HadError = TargetParser->MatchInstruction(IDLoc, Operands, Instrs); + assert (!HadError && "Unexpected error matching instruction"); + assert ((Instrs.size() == 1) && "Expected only a single instruction."); + + // Get the instruction descriptor. + llvm::MCInst Inst = Instrs[0]; + const llvm::MCInstrInfo *MII = TheTarget->createMCInstrInfo(); + const llvm::MCInstrDesc &Desc = MII->get(Inst.getOpcode()); + llvm::MCInstPrinter *IP = + TheTarget->createMCInstPrinter(1, *MAI, *MII, *MRI, *STI); + + // Build the list of clobbers. + for (unsigned i = 0, e = Desc.getNumDefs(); i != e; ++i) { + const llvm::MCOperand &Op = Inst.getOperand(i); + if (!Op.isReg()) + continue; + + std::string Reg; + llvm::raw_string_ostream OS(Reg); + IP->printRegName(OS, Op.getReg()); + + StringRef Clobber(OS.str()); + if (!Context.getTargetInfo().isValidClobber(Clobber)) + return StmtError(Diag(AsmLoc, diag::err_asm_unknown_register_name) << + Clobber); + ClobberRegs.insert(Reg); + } } + for (std::set<std::string>::iterator I = ClobberRegs.begin(), + E = ClobberRegs.end(); I != E; ++I) + Clobbers.push_back(*I); MSAsmStmt *NS = - new (Context) MSAsmStmt(Context, AsmLoc, IsSimple, /* IsVolatile */ true, - AsmToks, LineEnds, AsmString, Clobbers, EndLoc); - + new (Context) MSAsmStmt(Context, AsmLoc, LBraceLoc, IsSimple, + /*IsVolatile*/ true, AsmToks, Inputs, Outputs, + AsmString, Clobbers, EndLoc); return Owned(NS); } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index c8e45016671d..4dbf3e45b387 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -5518,6 +5518,13 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, if (Attr) ProcessDeclAttributeList(S, Specialization, Attr); + // Add alignment attributes if necessary; these attributes are checked when + // the ASTContext lays out the structure. + if (TUK == TUK_Definition) { + AddAlignmentAttributesForRecord(Specialization); + AddMsStructLayoutForRecord(Specialization); + } + if (ModulePrivateLoc.isValid()) Diag(Specialization->getLocation(), diag::err_module_private_specialization) << (isPartialSpecialization? 1 : 0) diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index c7cbc41b3035..20e755fdaee1 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -988,12 +988,11 @@ TemplateInstantiator::RebuildElaboratedType(SourceLocation KeywordLoc, SourceLocation TagLocation = KeywordLoc; - // FIXME: type might be anonymous. IdentifierInfo *Id = TD->getIdentifier(); // TODO: should we even warn on struct/class mismatches for this? Seems // like it's likely to produce a lot of spurious errors. - if (Keyword != ETK_None && Keyword != ETK_Typename) { + if (Id && Keyword != ETK_None && Keyword != ETK_Typename) { TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForKeyword(Keyword); if (!SemaRef.isAcceptableTagRedeclaration(TD, Kind, /*isDefinition*/false, TagLocation, *Id)) { diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 20fe036348ba..54f8dbaf0127 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -2258,6 +2258,51 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, ASM = ArrayType::Normal; D.setInvalidType(true); } + + // C99 6.7.5.2p1: The optional type qualifiers and the keyword static + // shall appear only in a declaration of a function parameter with an + // array type, ... + if (ASM == ArrayType::Static || ATI.TypeQuals) { + if (!(D.isPrototypeContext() || + D.getContext() == Declarator::KNRTypeListContext)) { + S.Diag(DeclType.Loc, diag::err_array_static_outside_prototype) << + (ASM == ArrayType::Static ? "'static'" : "type qualifier"); + // Remove the 'static' and the type qualifiers. + if (ASM == ArrayType::Static) + ASM = ArrayType::Normal; + ATI.TypeQuals = 0; + D.setInvalidType(true); + } + + // C99 6.7.5.2p1: ... and then only in the outermost array type + // derivation. + unsigned x = chunkIndex; + while (x != 0) { + // Walk outwards along the declarator chunks. + x--; + const DeclaratorChunk &DC = D.getTypeObject(x); + switch (DC.Kind) { + case DeclaratorChunk::Paren: + continue; + case DeclaratorChunk::Array: + case DeclaratorChunk::Pointer: + case DeclaratorChunk::Reference: + case DeclaratorChunk::MemberPointer: + S.Diag(DeclType.Loc, diag::err_array_static_not_outermost) << + (ASM == ArrayType::Static ? "'static'" : "type qualifier"); + if (ASM == ArrayType::Static) + ASM = ArrayType::Normal; + ATI.TypeQuals = 0; + D.setInvalidType(true); + break; + case DeclaratorChunk::Function: + case DeclaratorChunk::BlockPointer: + // These are invalid anyway, so just ignore. + break; + } + } + } + T = S.BuildArrayType(T, ASM, ArraySize, ATI.TypeQuals, SourceRange(DeclType.Loc, DeclType.EndLoc), Name); break; @@ -3224,7 +3269,6 @@ namespace { assert(Chunk.Kind == DeclaratorChunk::Function); TL.setLocalRangeBegin(Chunk.Loc); TL.setLocalRangeEnd(Chunk.EndLoc); - TL.setTrailingReturn(Chunk.Fun.hasTrailingReturnType()); const DeclaratorChunk::FunctionTypeInfo &FTI = Chunk.Fun; for (unsigned i = 0, e = TL.getNumArgs(), tpi = 0; i != e; ++i) { diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 90d584037817..619ad330b95b 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1185,10 +1185,10 @@ public: /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. StmtResult RebuildMSAsmStmt(SourceLocation AsmLoc, + SourceLocation LBraceLoc, ArrayRef<Token> AsmToks, - ArrayRef<unsigned> LineEnds, SourceLocation EndLoc) { - return getSema().ActOnMSAsmStmt(AsmLoc, AsmToks, LineEnds, EndLoc); + return getSema().ActOnMSAsmStmt(AsmLoc, LBraceLoc, AsmToks, EndLoc); } /// \brief Build a new Objective-C \@try statement. @@ -1277,12 +1277,11 @@ public: /// By default, performs semantic analysis to build the new statement. /// Subclasses may override this routine to provide different behavior. StmtResult RebuildObjCForCollectionStmt(SourceLocation ForLoc, - SourceLocation LParenLoc, Stmt *Element, Expr *Collection, SourceLocation RParenLoc, Stmt *Body) { - StmtResult ForEachStmt = getSema().ActOnObjCForCollectionStmt(ForLoc, LParenLoc, + StmtResult ForEachStmt = getSema().ActOnObjCForCollectionStmt(ForLoc, Element, Collection, RParenLoc); @@ -4205,7 +4204,7 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB, QualType ResultType; - if (TL.getTrailingReturn()) { + if (T->hasTrailingReturn()) { if (getDerived().TransformFunctionTypeParams(TL.getBeginLoc(), TL.getParmArray(), TL.getNumArgs(), @@ -4262,7 +4261,6 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc NewTL = TLB.push<FunctionProtoTypeLoc>(Result); NewTL.setLocalRangeBegin(TL.getLocalRangeBegin()); NewTL.setLocalRangeEnd(TL.getLocalRangeEnd()); - NewTL.setTrailingReturn(TL.getTrailingReturn()); for (unsigned i = 0, e = NewTL.getNumArgs(); i != e; ++i) NewTL.setArg(i, ParamDecls[i]); @@ -4286,7 +4284,6 @@ QualType TreeTransform<Derived>::TransformFunctionNoProtoType( FunctionNoProtoTypeLoc NewTL = TLB.push<FunctionNoProtoTypeLoc>(Result); NewTL.setLocalRangeBegin(TL.getLocalRangeBegin()); NewTL.setLocalRangeEnd(TL.getLocalRangeEnd()); - NewTL.setTrailingReturn(false); return Result; } @@ -5612,12 +5609,9 @@ StmtResult TreeTransform<Derived>::TransformMSAsmStmt(MSAsmStmt *S) { ArrayRef<Token> AsmToks = llvm::makeArrayRef(S->getAsmToks(), S->getNumAsmToks()); - ArrayRef<unsigned> LineEnds = - llvm::makeArrayRef(S->getLineEnds(), S->getNumLineEnds()); - // No need to transform the asm string literal. - return getDerived().RebuildMSAsmStmt(S->getAsmLoc(), AsmToks, LineEnds, - S->getEndLoc()); + return getDerived().RebuildMSAsmStmt(S->getAsmLoc(), S->getLBraceLoc(), + AsmToks, S->getEndLoc()); } template<typename Derived> @@ -5808,7 +5802,6 @@ TreeTransform<Derived>::TransformObjCForCollectionStmt( // Build a new statement. return getDerived().RebuildObjCForCollectionStmt(S->getForLoc(), - /*FIXME:*/S->getForLoc(), Element.get(), Collection.get(), S->getRParenLoc(), diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index beef338174f2..3adbc5783397 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -4259,7 +4259,6 @@ void TypeLocReader::VisitExtVectorTypeLoc(ExtVectorTypeLoc TL) { void TypeLocReader::VisitFunctionTypeLoc(FunctionTypeLoc TL) { TL.setLocalRangeBegin(ReadSourceLocation(Record, Idx)); TL.setLocalRangeEnd(ReadSourceLocation(Record, Idx)); - TL.setTrailingReturn(Record[Idx++]); for (unsigned i = 0, e = TL.getNumArgs(); i != e; ++i) { TL.setArg(i, ReadDeclAs<ParmVarDecl>(Record, Idx)); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index b7718c4655b4..425d2e32a7b1 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -486,7 +486,6 @@ void TypeLocWriter::VisitExtVectorTypeLoc(ExtVectorTypeLoc TL) { void TypeLocWriter::VisitFunctionTypeLoc(FunctionTypeLoc TL) { Writer.AddSourceLocation(TL.getLocalRangeBegin(), Record); Writer.AddSourceLocation(TL.getLocalRangeEnd(), Record); - Record.push_back(TL.getTrailingReturn()); for (unsigned i = 0, e = TL.getNumArgs(); i != e; ++i) Writer.AddDeclRef(TL.getArg(i), Record); } diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 30f45c7685b9..5edcf09f1150 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -235,17 +235,20 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, ProgramStateRef StNonNull, StNull; llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(L)); - // FIXME: Do we want to record the non-null assumption here? if (StNull && !StNonNull) { if (!BT_call_null) BT_call_null.reset( new BuiltinBug("Called function pointer is null (null dereference)")); emitBadCall(BT_call_null.get(), C, Callee); } + + C.addTransition(StNonNull); } void CallAndMessageChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + // If this is a call to a C++ method, check if the callee is null or // undefined. if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { @@ -258,11 +261,9 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, return; } - ProgramStateRef State = C.getState(); ProgramStateRef StNonNull, StNull; llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V)); - // FIXME: Do we want to record the non-null assumption here? if (StNull && !StNonNull) { if (!BT_cxx_call_null) BT_cxx_call_null.reset(new BuiltinBug("Called C++ object pointer " @@ -270,6 +271,8 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); return; } + + State = StNonNull; } // Don't check for uninitialized field values in arguments if the @@ -291,6 +294,9 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, Call.getArgExpr(i), /*IsFirstArgument=*/i == 0, checkUninitFields, Call, *BT)) return; + + // If we make it here, record our assumptions about the callee. + C.addTransition(State); } void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index fea57337bbda..b636efbe35a1 100644 --- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -25,7 +25,8 @@ using namespace ento; namespace { class DynamicTypePropagation: - public Checker< check::PostCall, + public Checker< check::PreCall, + check::PostCall, check::PostStmt<ImplicitCastExpr> > { const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, CheckerContext &C) const; @@ -34,11 +35,70 @@ class DynamicTypePropagation: const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, CheckerContext &C) const; public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkPostStmt(const ImplicitCastExpr *CastE, CheckerContext &C) const; }; } +static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, + CheckerContext &C) { + assert(Region); + assert(MD); + + ASTContext &Ctx = C.getASTContext(); + QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent())); + + ProgramStateRef State = C.getState(); + State = State->setDynamicTypeInfo(Region, Ty, /*CanBeSubclass=*/false); + C.addTransition(State); + return; +} + +void DynamicTypePropagation::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { + // C++11 [class.cdtor]p4: When a virtual function is called directly or + // indirectly from a constructor or from a destructor, including during + // the construction or destruction of the class’s non-static data members, + // and the object to which the call applies is the object under + // construction or destruction, the function called is the final overrider + // in the constructor's or destructor's class and not one overriding it in + // a more-derived class. + + switch (Ctor->getOriginExpr()->getConstructionKind()) { + case CXXConstructExpr::CK_Complete: + case CXXConstructExpr::CK_Delegating: + // No additional type info necessary. + return; + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: + if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) + recordFixedType(Target, Ctor->getDecl(), C); + return; + } + + return; + } + + if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { + // C++11 [class.cdtor]p4 (see above) + + const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion(); + if (!Target) + return; + + // FIXME: getRuntimeDefinition() can be expensive. It would be better to do + // this when we are entering the stack frame for the destructor. + const Decl *D = Dtor->getRuntimeDefinition().getDecl(); + if (!D) + return; + + recordFixedType(Target, cast<CXXDestructorDecl>(D), C); + return; + } +} + void DynamicTypePropagation::checkPostCall(const CallEvent &Call, CheckerContext &C) const { // We can obtain perfect type info for return values from some calls. @@ -82,6 +142,31 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call, break; } } + + return; + } + + if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { + // We may need to undo the effects of our pre-call check. + switch (Ctor->getOriginExpr()->getConstructionKind()) { + case CXXConstructExpr::CK_Complete: + case CXXConstructExpr::CK_Delegating: + // No additional work necessary. + // Note: This will leave behind the actual type of the object for + // complete constructors, but arguably that's a good thing, since it + // means the dynamic type info will be correct even for objects + // constructed with operator new. + return; + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: + if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) { + // We just finished a base constructor. Now we can use the subclass's + // type when resolving virtual calls. + const Decl *D = C.getLocationContext()->getDecl(); + recordFixedType(Target, cast<CXXConstructorDecl>(D), C); + } + return; + } } } diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 5503b23f4ae6..3c00d9915b4a 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1107,18 +1107,6 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) { if (S) break; - // Enable this code once the semantics of NSDeallocateObject are resolved - // for GC. <rdar://problem/6619988> -#if 0 - // Handle: NSDeallocateObject(id anObject); - // This method does allow 'nil' (although we don't check it now). - if (strcmp(FName, "NSDeallocateObject") == 0) { - return RetTy == Ctx.VoidTy - ? getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Dealloc) - : getPersistentStopSummary(); - } -#endif - if (RetTy->isPointerType()) { // For CoreFoundation ('CF') types. if (cocoa::isRefType(RetTy, "CF", FName)) { @@ -1591,28 +1579,12 @@ void RetainSummaryManager::InitializeMethodSummaries() { addClassMethSummary("NSWindow", "alloc", NoTrackYet); -#if 0 - addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", NULL); - - addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", "screen", NULL); -#endif - // For NSPanel (which subclasses NSWindow), allocated objects are not // self-owned. // FIXME: For now we don't track NSPanels. object for the same reason // as for NSWindow objects. addClassMethSummary("NSPanel", "alloc", NoTrackYet); -#if 0 - addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", NULL); - - addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", - "styleMask", "backing", "defer", "screen", NULL); -#endif - // Don't track allocated autorelease pools yet, as it is okay to prematurely // exit a method. addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); @@ -1636,62 +1608,6 @@ void RetainSummaryManager::InitializeMethodSummaries() { } //===----------------------------------------------------------------------===// -// AutoreleaseBindings - State used to track objects in autorelease pools. -//===----------------------------------------------------------------------===// -#define AUTORELEASE_POOL_MODELING (0) -// We do not currently have complete modeling of autorelease pools. - -#if AUTORELEASE_POOL_MODELING - -typedef llvm::ImmutableMap<SymbolRef, unsigned> ARCounts; -typedef llvm::ImmutableMap<SymbolRef, ARCounts> ARPoolContents; -typedef llvm::ImmutableList<SymbolRef> ARStack; - -static int AutoRCIndex = 0; -static int AutoRBIndex = 0; - -namespace { class AutoreleasePoolContents {}; } -namespace { class AutoreleaseStack {}; } - -namespace clang { -namespace ento { -template<> struct ProgramStateTrait<AutoreleaseStack> - : public ProgramStatePartialTrait<ARStack> { - static inline void *GDMIndex() { return &AutoRBIndex; } -}; - -template<> struct ProgramStateTrait<AutoreleasePoolContents> - : public ProgramStatePartialTrait<ARPoolContents> { - static inline void *GDMIndex() { return &AutoRCIndex; } -}; -} // end GR namespace -} // end clang namespace - -static SymbolRef GetCurrentAutoreleasePool(ProgramStateRef state) { - ARStack stack = state->get<AutoreleaseStack>(); - return stack.isEmpty() ? SymbolRef() : stack.getHead(); -} - -static ProgramStateRef -SendAutorelease(ProgramStateRef state, - ARCounts::Factory &F, - SymbolRef sym) { - SymbolRef pool = GetCurrentAutoreleasePool(state); - const ARCounts *cnts = state->get<AutoreleasePoolContents>(pool); - ARCounts newCnts(0); - - if (cnts) { - const unsigned *cnt = (*cnts).lookup(sym); - newCnts = F.add(*cnts, sym, cnt ? *cnt + 1 : 1); - } - else - newCnts = F.add(F.getEmptyMap(), sym, 1); - - return state->set<AutoreleasePoolContents>(pool, newCnts); -} -#endif - -//===----------------------------------------------------------------------===// // Error reporting. //===----------------------------------------------------------------------===// namespace { @@ -2431,11 +2347,6 @@ class RetainCountChecker mutable OwningPtr<RetainSummaryManager> Summaries; mutable OwningPtr<RetainSummaryManager> SummariesGC; - -#if AUTORELEASE_POOL_MODELING - mutable ARCounts::Factory ARCountFactory; -#endif - mutable SummaryLogTy SummaryLog; mutable bool ShouldResetSummaryLog; @@ -2892,15 +2803,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ, ResultTy)); // FIXME: Add a flag to the checker where allocations are assumed to - // *not* fail. (The code below is out-of-date, though.) -#if 0 - if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) { - bool isFeasible; - state = state.assume(loc::SymbolVal(Sym), true, isFeasible); - assert(isFeasible && "Cannot assume fresh symbol is non-null."); - } -#endif - + // *not* fail. break; } @@ -3011,9 +2914,6 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, case NewAutoreleasePool: assert(!C.isObjCGCEnabled()); -#if AUTORELEASE_POOL_MODELING - state = state->add<AutoreleaseStack>(sym); -#endif return state; case MayEscape: @@ -3030,13 +2930,7 @@ RetainCountChecker::updateSymbol(ProgramStateRef state, SymbolRef sym, case Autorelease: if (C.isObjCGCEnabled()) return state; - // Update the autorelease counts. - // TODO: AutoreleasePoolContents are not currently used. We will need to - // call SendAutorelease after it's wired up. -#if AUTORELEASE_POOL_MODELING - state = SendAutorelease(state, ARCountFactory, sym); -#endif V = V.autorelease(); break; @@ -3719,35 +3613,6 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, C.addTransition(state, Pred); } -//===----------------------------------------------------------------------===// -// Debug printing of refcount bindings and autorelease pools. -//===----------------------------------------------------------------------===// - -#if AUTORELEASE_POOL_MODELING -static void PrintPool(raw_ostream &Out, SymbolRef Sym, - ProgramStateRef State) { - Out << ' '; - if (Sym) - Sym->dumpToStream(Out); - else - Out << "<pool>"; - Out << ":{"; - - // Get the contents of the pool. - if (const ARCounts *Cnts = State->get<AutoreleasePoolContents>(Sym)) - for (ARCounts::iterator I = Cnts->begin(), E = Cnts->end(); I != E; ++I) - Out << '(' << I.getKey() << ',' << I.getData() << ')'; - Out << '}'; -} - -static bool UsesAutorelease(ProgramStateRef state) { - // A state uses autorelease if it allocated an autorelease pool or if it has - // objects in the caller's autorelease pool. - return !state->get<AutoreleaseStack>().isEmpty() || - state->get<AutoreleasePoolContents>(SymbolRef()); -} -#endif - void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { @@ -3761,20 +3626,6 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, I->second.print(Out); Out << NL; } - -#if AUTORELEASE_POOL_MODELING - // Print the autorelease stack. - if (UsesAutorelease(State)) { - Out << Sep << NL << "AR pool stack:"; - ARStack Stack = State->get<AutoreleaseStack>(); - - PrintPool(Out, SymbolRef(), State); // Print the caller's pool. - for (ARStack::iterator I = Stack.begin(), E = Stack.end(); I != E; ++I) - PrintPool(Out, *I, State); - - Out << NL; - } -#endif } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 5aac6406f69f..efeba17a62ba 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -16,7 +16,7 @@ void AnalysisManager::anchor() { } AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, const LangOptions &lang, - PathDiagnosticConsumer *pd, + const PathDiagnosticConsumers &PDC, StoreManagerCreator storemgr, ConstraintManagerCreator constraintmgr, CheckerManager *checkerMgr, @@ -33,7 +33,8 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, AnalysisInliningMode IMode, bool NoRetry) : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, /*addInitializers=*/true), - Ctx(ctx), Diags(diags), LangOpts(lang), PD(pd), + Ctx(ctx), Diags(diags), LangOpts(lang), + PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), MaxNodes(maxnodes), MaxVisit(maxvisit), @@ -49,29 +50,19 @@ AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); } -AnalysisManager::AnalysisManager(ASTContext &ctx, DiagnosticsEngine &diags, - AnalysisManager &ParentAM) - : AnaCtxMgr(ParentAM.AnaCtxMgr.getUseUnoptimizedCFG(), - ParentAM.AnaCtxMgr.getCFGBuildOptions().AddImplicitDtors, - ParentAM.AnaCtxMgr.getCFGBuildOptions().AddInitializers), - Ctx(ctx), Diags(diags), - LangOpts(ParentAM.LangOpts), PD(ParentAM.getPathDiagnosticConsumer()), - CreateStoreMgr(ParentAM.CreateStoreMgr), - CreateConstraintMgr(ParentAM.CreateConstraintMgr), - CheckerMgr(ParentAM.CheckerMgr), - MaxNodes(ParentAM.MaxNodes), - MaxVisit(ParentAM.MaxVisit), - VisualizeEGDot(ParentAM.VisualizeEGDot), - VisualizeEGUbi(ParentAM.VisualizeEGUbi), - PurgeDead(ParentAM.PurgeDead), - EagerlyAssume(ParentAM.EagerlyAssume), - TrimGraph(ParentAM.TrimGraph), - EagerlyTrimEGraph(ParentAM.EagerlyTrimEGraph), - IPAMode(ParentAM.IPAMode), - InlineMaxStackDepth(ParentAM.InlineMaxStackDepth), - InlineMaxFunctionSize(ParentAM.InlineMaxFunctionSize), - InliningMode(ParentAM.InliningMode), - NoRetryExhausted(ParentAM.NoRetryExhausted) -{ - AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); +AnalysisManager::~AnalysisManager() { + FlushDiagnostics(); + for (PathDiagnosticConsumers::iterator I = PathConsumers.begin(), + E = PathConsumers.end(); I != E; ++I) { + delete *I; + } +} + +void AnalysisManager::FlushDiagnostics() { + PathDiagnosticConsumer::FilesMade filesMade; + for (PathDiagnosticConsumers::iterator I = PathConsumers.begin(), + E = PathConsumers.end(); + I != E; ++I) { + (*I)->FlushDiagnostics(&filesMade); + } } diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 7ba2fa7fdd0d..571baecd729d 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -1345,6 +1345,9 @@ BugReport::~BugReport() { for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) { delete *I; } + while (!interestingSymbols.empty()) { + popInterestingSymbolsAndRegions(); + } } const Decl *BugReport::getDeclWithIssue() const { @@ -1386,11 +1389,11 @@ void BugReport::markInteresting(SymbolRef sym) { return; // If the symbol wasn't already in our set, note a configuration change. - if (interestingSymbols.insert(sym).second) + if (getInterestingSymbols().insert(sym).second) ++ConfigurationChangeToken; if (const SymbolMetadata *meta = dyn_cast<SymbolMetadata>(sym)) - interestingRegions.insert(meta->getRegion()); + getInterestingRegions().insert(meta->getRegion()); } void BugReport::markInteresting(const MemRegion *R) { @@ -1399,11 +1402,11 @@ void BugReport::markInteresting(const MemRegion *R) { // If the base region wasn't already in our set, note a configuration change. R = R->getBaseRegion(); - if (interestingRegions.insert(R).second) + if (getInterestingRegions().insert(R).second) ++ConfigurationChangeToken; if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) - interestingSymbols.insert(SR->getSymbol()); + getInterestingSymbols().insert(SR->getSymbol()); } void BugReport::markInteresting(SVal V) { @@ -1411,30 +1414,58 @@ void BugReport::markInteresting(SVal V) { markInteresting(V.getAsSymbol()); } -bool BugReport::isInteresting(SVal V) const { +bool BugReport::isInteresting(SVal V) { return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol()); } -bool BugReport::isInteresting(SymbolRef sym) const { +bool BugReport::isInteresting(SymbolRef sym) { if (!sym) return false; // We don't currently consider metadata symbols to be interesting // even if we know their region is interesting. Is that correct behavior? - return interestingSymbols.count(sym); + return getInterestingSymbols().count(sym); } -bool BugReport::isInteresting(const MemRegion *R) const { +bool BugReport::isInteresting(const MemRegion *R) { if (!R) return false; R = R->getBaseRegion(); - bool b = interestingRegions.count(R); + bool b = getInterestingRegions().count(R); if (b) return true; if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) - return interestingSymbols.count(SR->getSymbol()); + return getInterestingSymbols().count(SR->getSymbol()); return false; } - + +void BugReport::lazyInitializeInterestingSets() { + if (interestingSymbols.empty()) { + interestingSymbols.push_back(new Symbols()); + interestingRegions.push_back(new Regions()); + } +} + +BugReport::Symbols &BugReport::getInterestingSymbols() { + lazyInitializeInterestingSets(); + return *interestingSymbols.back(); +} + +BugReport::Regions &BugReport::getInterestingRegions() { + lazyInitializeInterestingSets(); + return *interestingRegions.back(); +} + +void BugReport::pushInterestingSymbolsAndRegions() { + interestingSymbols.push_back(new Symbols(getInterestingSymbols())); + interestingRegions.push_back(new Regions(getInterestingRegions())); +} + +void BugReport::popInterestingSymbolsAndRegions() { + delete interestingSymbols.back(); + interestingSymbols.pop_back(); + delete interestingRegions.back(); + interestingRegions.pop_back(); +} const Stmt *BugReport::getStmt() const { if (!ErrorNode) @@ -1793,12 +1824,13 @@ static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) { } void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, - SmallVectorImpl<BugReport *> &bugReports) { + PathDiagnosticConsumer &PC, + ArrayRef<BugReport *> &bugReports) { assert(!bugReports.empty()); SmallVector<const ExplodedNode *, 10> errorNodes; - for (SmallVectorImpl<BugReport*>::iterator I = bugReports.begin(), - E = bugReports.end(); I != E; ++I) { + for (ArrayRef<BugReport*>::iterator I = bugReports.begin(), + E = bugReports.end(); I != E; ++I) { errorNodes.push_back((*I)->getErrorNode()); } @@ -1818,8 +1850,7 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, const ExplodedNode *N = GPair.second.first; // Start building the path diagnostic... - PathDiagnosticBuilder PDB(*this, R, BackMap.get(), - getPathDiagnosticConsumer()); + PathDiagnosticBuilder PDB(*this, R, BackMap.get(), &PC); // Register additional node visitors. R->addVisitor(new NilReceiverBRVisitor()); @@ -1867,6 +1898,8 @@ void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, case PathDiagnosticConsumer::Minimal: GenerateMinimalPathDiagnostic(PD, PDB, N, visitors); break; + case PathDiagnosticConsumer::None: + llvm_unreachable("PathDiagnosticConsumer::None should never appear here"); } // Clean up the visitors we used. @@ -2022,53 +2055,21 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, return exampleReport; } -//===----------------------------------------------------------------------===// -// DiagnosticCache. This is a hack to cache analyzer diagnostics. It -// uses global state, which eventually should go elsewhere. -//===----------------------------------------------------------------------===// -namespace { -class DiagCacheItem : public llvm::FoldingSetNode { - llvm::FoldingSetNodeID ID; -public: - DiagCacheItem(BugReport *R, PathDiagnostic *PD) { - R->Profile(ID); - PD->Profile(ID); - } - - void Profile(llvm::FoldingSetNodeID &id) { - id = ID; - } - - llvm::FoldingSetNodeID &getID() { return ID; } -}; -} - -static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) { - // FIXME: Eventually this diagnostic cache should reside in something - // like AnalysisManager instead of being a static variable. This is - // really unsafe in the long term. - typedef llvm::FoldingSet<DiagCacheItem> DiagnosticCache; - static DiagnosticCache DC; - - void *InsertPos; - DiagCacheItem *Item = new DiagCacheItem(R, PD); - - if (DC.FindNodeOrInsertPos(Item->getID(), InsertPos)) { - delete Item; - return true; - } - - DC.InsertNode(Item, InsertPos); - return false; -} - void BugReporter::FlushReport(BugReportEquivClass& EQ) { SmallVector<BugReport*, 10> bugReports; BugReport *exampleReport = FindReportInEquivalenceClass(EQ, bugReports); - if (!exampleReport) - return; - - PathDiagnosticConsumer* PD = getPathDiagnosticConsumer(); + if (exampleReport) { + const PathDiagnosticConsumers &C = getPathDiagnosticConsumers(); + for (PathDiagnosticConsumers::const_iterator I=C.begin(), + E=C.end(); I != E; ++I) { + FlushReport(exampleReport, **I, bugReports); + } + } +} + +void BugReporter::FlushReport(BugReport *exampleReport, + PathDiagnosticConsumer &PD, + ArrayRef<BugReport*> bugReports) { // FIXME: Make sure we use the 'R' for the path that was actually used. // Probably doesn't make a difference in practice. @@ -2077,65 +2078,39 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { OwningPtr<PathDiagnostic> D(new PathDiagnostic(exampleReport->getDeclWithIssue(), exampleReport->getBugType().getName(), - !PD || PD->useVerboseDescription() + PD.useVerboseDescription() ? exampleReport->getDescription() : exampleReport->getShortDescription(), BT.getCategory())); - if (!bugReports.empty()) - GeneratePathDiagnostic(*D.get(), bugReports); - - // Get the meta data. - const BugReport::ExtraTextList &Meta = - exampleReport->getExtraText(); - for (BugReport::ExtraTextList::const_iterator i = Meta.begin(), - e = Meta.end(); i != e; ++i) { - D->addMeta(*i); - } - - // Emit a summary diagnostic to the regular Diagnostics engine. - BugReport::ranges_iterator Beg, End; - llvm::tie(Beg, End) = exampleReport->getRanges(); - DiagnosticsEngine &Diag = getDiagnostic(); - - if (!IsCachedDiagnostic(exampleReport, D.get())) { - // Search the description for '%', as that will be interpretted as a - // format character by FormatDiagnostics. - StringRef desc = exampleReport->getShortDescription(); - - SmallString<512> TmpStr; - llvm::raw_svector_ostream Out(TmpStr); - for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) { - if (*I == '%') - Out << "%%"; - else - Out << *I; - } - - Out.flush(); - unsigned ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, TmpStr); - - DiagnosticBuilder diagBuilder = Diag.Report( - exampleReport->getLocation(getSourceManager()).asLocation(), ErrorDiag); - for (BugReport::ranges_iterator I = Beg; I != End; ++I) - diagBuilder << *I; + // Generate the full path diagnostic, using the generation scheme + // specified by the PathDiagnosticConsumer. + if (PD.getGenerationScheme() != PathDiagnosticConsumer::None) { + if (!bugReports.empty()) + GeneratePathDiagnostic(*D.get(), PD, bugReports); } - // Emit a full diagnostic for the path if we have a PathDiagnosticConsumer. - if (!PD) - return; - + // If the path is empty, generate a single step path with the location + // of the issue. if (D->path.empty()) { - PathDiagnosticPiece *piece = new PathDiagnosticEventPiece( - exampleReport->getLocation(getSourceManager()), - exampleReport->getDescription()); + PathDiagnosticLocation L = exampleReport->getLocation(getSourceManager()); + PathDiagnosticPiece *piece = + new PathDiagnosticEventPiece(L, exampleReport->getDescription()); + BugReport::ranges_iterator Beg, End; + llvm::tie(Beg, End) = exampleReport->getRanges(); for ( ; Beg != End; ++Beg) piece->addRange(*Beg); - D->getActivePath().push_back(piece); } - PD->HandlePathDiagnostic(D.take()); + // Get the meta data. + const BugReport::ExtraTextList &Meta = exampleReport->getExtraText(); + for (BugReport::ExtraTextList::const_iterator i = Meta.begin(), + e = Meta.end(); i != e; ++i) { + D->addMeta(*i); + } + + PD.HandlePathDiagnostic(D.take()); } void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 46aa9e2b9123..e7295878fa6f 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -323,7 +323,7 @@ void bugreporter::addTrackNullOrUndefValueVisitor(const ExplodedNode *N, // Walk through lvalue-to-rvalue conversions. const Expr *Ex = dyn_cast<Expr>(S); if (Ex) { - Ex = Ex->IgnoreParenLValueCasts(); + Ex = Ex->IgnoreParenCasts(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { const VarRegion *R = diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index e3f4c61e905e..5345bd517045 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -207,10 +207,14 @@ SourceRange CallEvent::getArgSourceRange(unsigned Index) const { return ArgE->getSourceRange(); } +void CallEvent::dump() const { + dump(llvm::errs()); +} + void CallEvent::dump(raw_ostream &Out) const { ASTContext &Ctx = getState()->getStateManager().getContext(); if (const Expr *E = getOriginExpr()) { - E->printPretty(Out, Ctx, 0, Ctx.getPrintingPolicy()); + E->printPretty(Out, 0, Ctx.getPrintingPolicy()); Out << "\n"; return; } @@ -372,47 +376,49 @@ void CXXInstanceCall::getExtraInvalidatedRegions(RegionList &Regions) const { Regions.push_back(R); } -static const CXXMethodDecl *devirtualize(const CXXMethodDecl *MD, SVal ThisVal){ - const MemRegion *R = ThisVal.getAsRegion(); - if (!R) - return 0; - - const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R->StripCasts()); - if (!TR) - return 0; - - const CXXRecordDecl *RD = TR->getValueType()->getAsCXXRecordDecl(); - if (!RD) - return 0; - - const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD); - const FunctionDecl *Definition; - if (!Result->hasBody(Definition)) - return 0; - - return cast<CXXMethodDecl>(Definition); -} - RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { + // Do we have a decl at all? const Decl *D = getDecl(); if (!D) return RuntimeDefinition(); + // If the method is non-virtual, we know we can inline it. const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); if (!MD->isVirtual()) return AnyFunctionCall::getRuntimeDefinition(); - // If the method is virtual, see if we can find the actual implementation - // based on context-sensitivity. - // FIXME: Virtual method calls behave differently when an object is being - // constructed or destructed. It's not as simple as "no devirtualization" - // because a /partially/ constructed object can be referred to through a - // base pointer. We'll eventually want to use DynamicTypeInfo here. - if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal())) - return RuntimeDefinition(Devirtualized); + // Do we know the implicit 'this' object being called? + const MemRegion *R = getCXXThisVal().getAsRegion(); + if (!R) + return RuntimeDefinition(); - return RuntimeDefinition(); + // Do we know anything about the type of 'this'? + DynamicTypeInfo DynType = getState()->getDynamicTypeInfo(R); + if (!DynType.isValid()) + return RuntimeDefinition(); + + // Is the type a C++ class? (This is mostly a defensive check.) + QualType RegionType = DynType.getType()->getPointeeType(); + const CXXRecordDecl *RD = RegionType->getAsCXXRecordDecl(); + if (!RD || !RD->hasDefinition()) + return RuntimeDefinition(); + + // Find the decl for this method in that class. + const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD, true); + assert(Result && "At the very least the static decl should show up."); + + // Does the decl that we found have an implementation? + const FunctionDecl *Definition; + if (!Result->hasBody(Definition)) + return RuntimeDefinition(); + + // We found a definition. If we're not sure that this devirtualization is + // actually what will happen at runtime, make sure to provide the region so + // that ExprEngine can decide what to do with it. + if (DynType.canBeASubClass()) + return RuntimeDefinition(Definition, R->StripCasts()); + return RuntimeDefinition(Definition, /*DispatchRegion=*/0); } void CXXInstanceCall::getInitialStackFrameContents( @@ -421,16 +427,17 @@ void CXXInstanceCall::getInitialStackFrameContents( AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings); // Handle the binding of 'this' in the new stack frame. - // We need to make sure we have the proper layering of CXXBaseObjectRegions. SVal ThisVal = getCXXThisVal(); if (!ThisVal.isUnknown()) { ProgramStateManager &StateMgr = getState()->getStateManager(); SValBuilder &SVB = StateMgr.getSValBuilder(); - + const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); - if (const MemRegion *ThisReg = ThisVal.getAsRegion()) { + // If we devirtualized to a different member function, we need to make sure + // we have the proper layering of CXXBaseObjectRegions. + if (MD->getCanonicalDecl() != getDecl()->getCanonicalDecl()) { ASTContext &Ctx = SVB.getContext(); const CXXRecordDecl *Class = MD->getParent(); QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class)); @@ -439,13 +446,10 @@ void CXXInstanceCall::getInitialStackFrameContents( bool Failed; ThisVal = StateMgr.getStoreManager().evalDynamicCast(ThisVal, Ty, Failed); assert(!Failed && "Calling an incorrectly devirtualized method"); - - // If we couldn't build the correct cast, just strip off all casts. - if (ThisVal.isUnknown()) - ThisVal = loc::MemRegionVal(ThisReg->StripCasts()); } - Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); + if (!ThisVal.isUnknown()) + Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); } } @@ -666,6 +670,9 @@ bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, if (InterfLoc.isValid() && SM.isFromMainFile(InterfLoc)) return false; + // Assume that property accessors are not overridden. + if (getMessageKind() == OCM_PropertyAccess) + return false; // We assume that if the method is public (declared outside of main file) or // has a parent which publicly declares the method, the method could be @@ -853,4 +860,3 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx, return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(), State, CallerCtx); } - diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 8ee6723057a6..3b2e4ec8243b 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -154,6 +154,11 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { } } + // Generate a CallEvent /before/ cleaning the state, so that we can get the + // correct value for 'this' (if necessary). + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<> Call = CEMgr.getCaller(calleeCtx, state); + // Step 3: BindedRetNode -> CleanedNodes // If we can find a statement and a block in the inlined function, run remove // dead bindings before returning from the call. This is important to ensure @@ -203,21 +208,21 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) { &Ctx); SaveAndRestore<unsigned> CBISave(currentStmtIdx, calleeCtx->getIndex()); - CallEventManager &CEMgr = getStateManager().getCallEventManager(); - CallEventRef<> Call = CEMgr.getCaller(calleeCtx, CEEState); + CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState); ExplodedNodeSet DstPostCall; - getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, *Call, - *this, true); + getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, + *UpdatedCall, *this, + /*WasInlined=*/true); ExplodedNodeSet Dst; - if (isa<ObjCMethodCall>(Call)) { - getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, - cast<ObjCMethodCall>(*Call), - *this, true); + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { + getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg, + *this, + /*WasInlined=*/true); } else if (CE) { getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, - *this, true); + *this, /*WasInlined=*/true); } else { Dst.insert(DstPostCall); } @@ -555,12 +560,20 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, RuntimeDefinition RD = Call->getRuntimeDefinition(); const Decl *D = RD.getDecl(); if (D) { - // Explore with and without inlining the call. - if (RD.mayHaveOtherDefinitions() && - getAnalysisManager().IPAMode == DynamicDispatchBifurcate) { - BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); - return; + if (RD.mayHaveOtherDefinitions()) { + // Explore with and without inlining the call. + if (getAnalysisManager().IPAMode == DynamicDispatchBifurcate) { + BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); + return; + } + + // Don't inline if we're not in any dynamic dispatch mode. + if (getAnalysisManager().IPAMode != DynamicDispatch) { + conservativeEvalCall(*Call, Bldr, Pred, State); + return; + } } + // We are not bifurcating and we do have a Decl, so just inline. if (inlineCall(*Call, D, Bldr, Pred, State)) return; @@ -575,6 +588,7 @@ void ExprEngine::BifurcateCall(const MemRegion *BifurReg, const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, ExplodedNode *Pred) { assert(BifurReg); + BifurReg = BifurReg->StripCasts(); // Check if we've performed the split already - note, we only want // to split the path once per memory region. diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 0152e328e508..982bcbfcdf9a 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -45,7 +45,7 @@ public: virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); } virtual void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade); + FilesMade *filesMade); virtual StringRef getName() const { return "HTMLDiagnostics"; @@ -63,7 +63,7 @@ public: const char *HighlightEnd = "</span>"); void ReportDiag(const PathDiagnostic& D, - SmallVectorImpl<std::string> *FilesMade); + FilesMade *filesMade); }; } // end anonymous namespace @@ -76,10 +76,10 @@ HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix, FilePrefix.appendComponent("report"); } -PathDiagnosticConsumer* -ento::createHTMLDiagnosticConsumer(const std::string& prefix, - const Preprocessor &PP) { - return new HTMLDiagnostics(prefix, PP); +void ento::createHTMLDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string& prefix, + const Preprocessor &PP) { + C.push_back(new HTMLDiagnostics(prefix, PP)); } //===----------------------------------------------------------------------===// @@ -88,15 +88,15 @@ ento::createHTMLDiagnosticConsumer(const std::string& prefix, void HTMLDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade) { + FilesMade *filesMade) { for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), et = Diags.end(); it != et; ++it) { - ReportDiag(**it, FilesMade); + ReportDiag(**it, filesMade); } } void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, - SmallVectorImpl<std::string> *FilesMade) { + FilesMade *filesMade) { // Create the HTML directory if it is missing. if (!createdDir) { @@ -266,8 +266,10 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, return; } - if (FilesMade) - FilesMade->push_back(llvm::sys::path::filename(H.str())); + if (filesMade) { + filesMade->push_back(std::make_pair(StringRef(getName()), + llvm::sys::path::filename(H.str()))); + } // Emit the HTML to disk. for (RewriteBuffer::iterator I = Buf->begin(), E = Buf->end(); I!=E; ++I) @@ -480,29 +482,11 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, R.InsertTextBefore(Loc, os.str()); // Now highlight the ranges. - for (const SourceRange *I = P.ranges_begin(), *E = P.ranges_end(); - I != E; ++I) + ArrayRef<SourceRange> Ranges = P.getRanges(); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); I != E; ++I) { HighlightRange(R, LPosInfo.first, *I); - -#if 0 - // If there is a code insertion hint, insert that code. - // FIXME: This code is disabled because it seems to mangle the HTML - // output. I'm leaving it here because it's generally the right idea, - // but needs some help from someone more familiar with the rewriter. - for (const FixItHint *Hint = P.fixit_begin(), *HintEnd = P.fixit_end(); - Hint != HintEnd; ++Hint) { - if (Hint->RemoveRange.isValid()) { - HighlightRange(R, LPosInfo.first, Hint->RemoveRange, - "<span class=\"CodeRemovalHint\">", "</span>"); - } - if (Hint->InsertionLoc.isValid()) { - std::string EscapedCode = html::EscapeText(Hint->CodeToInsert, true); - EscapedCode = "<span class=\"CodeInsertionHint\">" + EscapedCode - + "</span>"; - R.InsertTextBefore(Hint->InsertionLoc, EscapedCode); - } } -#endif } static void EmitAlphaCounter(raw_ostream &os, unsigned n) { diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 7d52aac71c25..c849778e7f23 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -157,13 +157,13 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(PathDiagnostic *D) { return; // FIXME: Emit a warning? // Check the source ranges. - for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), - RE = piece->ranges_end(); - RI != RE; ++RI) { - SourceLocation L = SMgr.getExpansionLoc(RI->getBegin()); + ArrayRef<SourceRange> Ranges = piece->getRanges(); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); I != E; ++I) { + SourceLocation L = SMgr.getExpansionLoc(I->getBegin()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? - L = SMgr.getExpansionLoc(RI->getEnd()); + L = SMgr.getExpansionLoc(I->getEnd()); if (!L.isFileID() || SMgr.getFileID(L) != FID) return; // FIXME: Emit a warning? } @@ -240,8 +240,8 @@ struct CompareDiagnostics { }; } -void -PathDiagnosticConsumer::FlushDiagnostics(SmallVectorImpl<std::string> *Files) { +void PathDiagnosticConsumer::FlushDiagnostics( + PathDiagnosticConsumer::FilesMade *Files) { if (flushed) return; @@ -718,7 +718,9 @@ void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddString(str); // FIXME: Add profiling support for code hints. ID.AddInteger((unsigned) getDisplayHint()); - for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) { + ArrayRef<SourceRange> Ranges = getRanges(); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); + I != E; ++I) { ID.AddInteger(I->getBegin().getRawEncoding()); ID.AddInteger(I->getEnd().getRawEncoding()); } diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 58a4bba9483f..d5fdd9d2bb99 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -30,23 +30,21 @@ namespace { class PlistDiagnostics : public PathDiagnosticConsumer { const std::string OutputFile; const LangOptions &LangOpts; - OwningPtr<PathDiagnosticConsumer> SubPD; const bool SupportsCrossFileDiagnostics; public: PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, - bool supportsMultipleFiles, - PathDiagnosticConsumer *subPD); + bool supportsMultipleFiles); virtual ~PlistDiagnostics() {} void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade); + FilesMade *filesMade); virtual StringRef getName() const { return "PlistDiagnostics"; } - PathGenerationScheme getGenerationScheme() const; + PathGenerationScheme getGenerationScheme() const { return Extensive; } bool supportsLogicalOpControlFlow() const { return true; } bool supportsAllBlockEdges() const { return true; } virtual bool useVerboseDescription() const { return false; } @@ -58,29 +56,20 @@ namespace { PlistDiagnostics::PlistDiagnostics(const std::string& output, const LangOptions &LO, - bool supportsMultipleFiles, - PathDiagnosticConsumer *subPD) - : OutputFile(output), LangOpts(LO), SubPD(subPD), + bool supportsMultipleFiles) + : OutputFile(output), LangOpts(LO), SupportsCrossFileDiagnostics(supportsMultipleFiles) {} -PathDiagnosticConsumer* -ento::createPlistDiagnosticConsumer(const std::string& s, const Preprocessor &PP, - PathDiagnosticConsumer *subPD) { - return new PlistDiagnostics(s, PP.getLangOpts(), false, subPD); +void ento::createPlistDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string& s, + const Preprocessor &PP) { + C.push_back(new PlistDiagnostics(s, PP.getLangOpts(), false)); } -PathDiagnosticConsumer* -ento::createPlistMultiFileDiagnosticConsumer(const std::string &s, - const Preprocessor &PP) { - return new PlistDiagnostics(s, PP.getLangOpts(), true, 0); -} - -PathDiagnosticConsumer::PathGenerationScheme -PlistDiagnostics::getGenerationScheme() const { - if (const PathDiagnosticConsumer *PD = SubPD.get()) - return PD->getGenerationScheme(); - - return Extensive; +void ento::createPlistMultiFileDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string &s, + const Preprocessor &PP) { + C.push_back(new PlistDiagnostics(s, PP.getLangOpts(), true)); } static void AddFID(FIDMap &FIDs, SmallVectorImpl<FileID> &V, @@ -231,15 +220,16 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P, EmitLocation(o, SM, LangOpts, L, FM, indent); // Output the ranges (if any). - PathDiagnosticPiece::range_iterator RI = P.ranges_begin(), - RE = P.ranges_end(); + ArrayRef<SourceRange> Ranges = P.getRanges(); - if (RI != RE) { + if (!Ranges.empty()) { Indent(o, indent) << "<key>ranges</key>\n"; Indent(o, indent) << "<array>\n"; ++indent; - for (; RI != RE; ++RI) - EmitRange(o, SM, LangOpts, *RI, FM, indent+1); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); + I != E; ++I) { + EmitRange(o, SM, LangOpts, *I, FM, indent+1); + } --indent; Indent(o, indent) << "</array>\n"; } @@ -353,7 +343,7 @@ static void ReportPiece(raw_ostream &o, void PlistDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade) { + FilesMade *filesMade) { // Build up a set of FIDs that we use by scanning the locations and // ranges of the diagnostics. FIDMap FM; @@ -380,11 +370,11 @@ void PlistDiagnostics::FlushDiagnosticsImpl( I!=E; ++I) { const PathDiagnosticPiece *piece = I->getPtr(); AddFID(FM, Fids, SM, piece->getLocation().asLocation()); - - for (PathDiagnosticPiece::range_iterator RI = piece->ranges_begin(), - RE= piece->ranges_end(); RI != RE; ++RI) { - AddFID(FM, Fids, SM, RI->getBegin()); - AddFID(FM, Fids, SM, RI->getEnd()); + ArrayRef<SourceRange> Ranges = piece->getRanges(); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); I != E; ++I) { + AddFID(FM, Fids, SM, I->getBegin()); + AddFID(FM, Fids, SM, I->getEnd()); } if (const PathDiagnosticCallPiece *call = @@ -507,19 +497,21 @@ void PlistDiagnostics::FlushDiagnosticsImpl( EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2); // Output the diagnostic to the sub-diagnostic client, if any. - if (SubPD) { - std::vector<const PathDiagnostic *> SubDiags; - SubDiags.push_back(D); - SmallVector<std::string, 1> SubFilesMade; - SubPD->FlushDiagnosticsImpl(SubDiags, &SubFilesMade); - - if (!SubFilesMade.empty()) { - o << " <key>" << SubPD->getName() << "_files</key>\n"; - o << " <array>\n"; - for (size_t i = 0, n = SubFilesMade.size(); i < n ; ++i) - o << " <string>" << SubFilesMade[i] << "</string>\n"; - o << " </array>\n"; + if (!filesMade->empty()) { + StringRef lastName; + for (FilesMade::iterator I = filesMade->begin(), E = filesMade->end(); + I != E; ++I) { + StringRef newName = I->first; + if (newName != lastName) { + if (!lastName.empty()) + o << " </array>\n"; + lastName = newName; + o << " <key>" << lastName << "_files</key>\n"; + o << " <array>\n"; + } + o << " <string>" << I->second << "</string>\n"; } + o << " </array>\n"; } // Close up the entry. @@ -531,6 +523,8 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // Finish. o << "</dict>\n</plist>"; - if (FilesMade) - FilesMade->push_back(OutputFile); + if (filesMade) { + StringRef Name(getName()); + filesMade->push_back(std::make_pair(Name, OutputFile)); + } } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index dc988cc5f1fc..2000338ee0a6 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -745,14 +745,16 @@ template<> struct ProgramStateTrait<DynamicTypeMap> }} DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const { + Reg = Reg->StripCasts(); + // Look up the dynamic type in the GDM. const DynamicTypeInfo *GDMType = get<DynamicTypeMap>(Reg); if (GDMType) return *GDMType; // Otherwise, fall back to what we know about the region. - if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(Reg)) - return DynamicTypeInfo(TR->getValueType()); + if (const TypedRegion *TR = dyn_cast<TypedRegion>(Reg)) + return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false); if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) { SymbolRef Sym = SR->getSymbol(); @@ -764,6 +766,7 @@ DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const { ProgramStateRef ProgramState::setDynamicTypeInfo(const MemRegion *Reg, DynamicTypeInfo NewTy) const { + Reg = Reg->StripCasts(); ProgramStateRef NewState = set<DynamicTypeMap>(Reg, NewTy); assert(NewState); return NewState; diff --git a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp index e5b8553aeda1..66bf4bb222d1 100644 --- a/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/TextPathDiagnostics.cpp @@ -32,7 +32,7 @@ public: : OutputFile(output), Diag(diag) {} void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade); + FilesMade *filesMade); virtual StringRef getName() const { return "TextPathDiagnostics"; @@ -47,15 +47,15 @@ public: } // end anonymous namespace -PathDiagnosticConsumer* -ento::createTextPathDiagnosticConsumer(const std::string& out, - const Preprocessor &PP) { - return new TextPathDiagnostics(out, PP.getDiagnostics()); +void ento::createTextPathDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string& out, + const Preprocessor &PP) { + C.push_back(new TextPathDiagnostics(out, PP.getDiagnostics())); } void TextPathDiagnostics::FlushDiagnosticsImpl( std::vector<const PathDiagnostic *> &Diags, - SmallVectorImpl<std::string> *FilesMade) { + FilesMade *) { for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(), et = Diags.end(); it != et; ++it) { const PathDiagnostic *D = *it; diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index fcdaaeaedf9e..34b5266e4b76 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -64,14 +64,55 @@ STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function."); // Special PathDiagnosticConsumers. //===----------------------------------------------------------------------===// -static PathDiagnosticConsumer* -createPlistHTMLDiagnosticConsumer(const std::string& prefix, - const Preprocessor &PP) { - PathDiagnosticConsumer *PD = - createHTMLDiagnosticConsumer(llvm::sys::path::parent_path(prefix), PP); - return createPlistDiagnosticConsumer(prefix, PP, PD); +static void createPlistHTMLDiagnosticConsumer(PathDiagnosticConsumers &C, + const std::string &prefix, + const Preprocessor &PP) { + createHTMLDiagnosticConsumer(C, llvm::sys::path::parent_path(prefix), PP); + createPlistDiagnosticConsumer(C, prefix, PP); } +namespace { +class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer { + DiagnosticsEngine &Diag; +public: + ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag) : Diag(Diag) {} + virtual ~ClangDiagPathDiagConsumer() {} + virtual StringRef getName() const { return "ClangDiags"; } + virtual bool useVerboseDescription() const { return false; } + virtual PathGenerationScheme getGenerationScheme() const { return None; } + + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + FilesMade *filesMade) { + for (std::vector<const PathDiagnostic*>::iterator I = Diags.begin(), + E = Diags.end(); I != E; ++I) { + const PathDiagnostic *PD = *I; + StringRef desc = PD->getDescription(); + SmallString<512> TmpStr; + llvm::raw_svector_ostream Out(TmpStr); + for (StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) { + if (*I == '%') + Out << "%%"; + else + Out << *I; + } + Out.flush(); + unsigned ErrorDiag = Diag.getCustomDiagID(DiagnosticsEngine::Warning, + TmpStr); + SourceLocation L = PD->getLocation().asLocation(); + DiagnosticBuilder diagBuilder = Diag.Report(L, ErrorDiag); + + // Get the ranges from the last point in the path. + ArrayRef<SourceRange> Ranges = PD->path.back()->getRanges(); + + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); I != E; ++I) { + diagBuilder << *I; + } + } + } +}; +} // end anonymous namespace + //===----------------------------------------------------------------------===// // AnalysisConsumer declaration. //===----------------------------------------------------------------------===// @@ -105,8 +146,8 @@ public: /// working with a PCH file. SetOfDecls LocalTUDecls; - // PD is owned by AnalysisManager. - PathDiagnosticConsumer *PD; + // Set of PathDiagnosticConsumers. Owned by AnalysisManager. + PathDiagnosticConsumers PathConsumers; StoreManagerCreator CreateStoreMgr; ConstraintManagerCreator CreateConstraintMgr; @@ -126,7 +167,7 @@ public: const AnalyzerOptions& opts, ArrayRef<std::string> plugins) : RecVisitorMode(ANALYSIS_ALL), RecVisitorBR(0), - Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins), PD(0) { + Ctx(0), PP(pp), OutDir(outdir), Opts(opts), Plugins(plugins) { DigestAnalyzerOptions(); if (Opts.PrintStats) { llvm::EnableStatistics(); @@ -141,17 +182,19 @@ public: void DigestAnalyzerOptions() { // Create the PathDiagnosticConsumer. + PathConsumers.push_back(new ClangDiagPathDiagConsumer(PP.getDiagnostics())); + if (!OutDir.empty()) { switch (Opts.AnalysisDiagOpt) { default: #define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \ - case PD_##NAME: PD = CREATEFN(OutDir, PP); break; + case PD_##NAME: CREATEFN(PathConsumers, OutDir, PP); break; #include "clang/Frontend/Analyses.def" } } else if (Opts.AnalysisDiagOpt == PD_TEXT) { // Create the text client even without a specified output file since // it just uses diagnostic notes. - PD = createTextPathDiagnosticConsumer("", PP); + createTextPathDiagnosticConsumer(PathConsumers, "", PP); } // Create the analyzer component creators. @@ -205,9 +248,12 @@ public: Ctx = &Context; checkerMgr.reset(createCheckerManager(Opts, PP.getLangOpts(), Plugins, PP.getDiagnostics())); - Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(), - PP.getLangOpts(), PD, - CreateStoreMgr, CreateConstraintMgr, + Mgr.reset(new AnalysisManager(*Ctx, + PP.getDiagnostics(), + PP.getLangOpts(), + PathConsumers, + CreateStoreMgr, + CreateConstraintMgr, checkerMgr.get(), Opts.MaxNodes, Opts.MaxLoop, Opts.VisualizeEGDot, Opts.VisualizeEGUbi, diff --git a/test/Analysis/CFNumber.c b/test/Analysis/CFNumber.c index fbbe4d15f49f..537e49785130 100644 --- a/test/Analysis/CFNumber.c +++ b/test/Analysis/CFNumber.c @@ -17,11 +17,11 @@ typedef const struct __CFNumber * CFNumberRef; extern CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType theType, const void *valuePtr); CFNumberRef f1(unsigned char x) { - return CFNumberCreate(0, kCFNumberSInt16Type, &x); // expected-warning{{An 8 bit integer is used to initialize a CFNumber object that represents a 16 bit integer. 8 bits of the CFNumber value will be garbage.}} + return CFNumberCreate(0, kCFNumberSInt16Type, &x); // expected-warning{{An 8 bit integer is used to initialize a CFNumber object that represents a 16 bit integer. 8 bits of the CFNumber value will be garbage}} } __attribute__((cf_returns_retained)) CFNumberRef f2(unsigned short x) { - return CFNumberCreate(0, kCFNumberSInt8Type, &x); // expected-warning{{A 16 bit integer is used to initialize a CFNumber object that represents an 8 bit integer. 8 bits of the input integer will be lost.}} + return CFNumberCreate(0, kCFNumberSInt8Type, &x); // expected-warning{{A 16 bit integer is used to initialize a CFNumber object that represents an 8 bit integer. 8 bits of the input integer will be lost}} } // test that the attribute overrides the naming convention. @@ -30,5 +30,5 @@ __attribute__((cf_returns_not_retained)) CFNumberRef CreateNum(unsigned char x) } CFNumberRef f3(unsigned i) { - return CFNumberCreate(0, kCFNumberLongType, &i); // expected-warning{{A 32 bit integer is used to initialize a CFNumber object that represents a 64 bit integer.}} + return CFNumberCreate(0, kCFNumberLongType, &i); // expected-warning{{A 32 bit integer is used to initialize a CFNumber object that represents a 64 bit integer}} } diff --git a/test/Analysis/CheckNSError.m b/test/Analysis/CheckNSError.m index d35b686ef1d9..cdec1d50f2ca 100644 --- a/test/Analysis/CheckNSError.m +++ b/test/Analysis/CheckNSError.m @@ -23,7 +23,7 @@ extern NSString * const NSXMLParserErrorDomain ; @implementation A - (void)myMethodWhichMayFail:(NSError **)error { // expected-warning {{Method accepting NSError** should have a non-void return value to indicate whether or not an error occurred}} - *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // expected-warning {{Potential null dereference.}} + *error = [NSError errorWithDomain:@"domain" code:1 userInfo:0]; // expected-warning {{Potential null dereference}} } - (BOOL)myMethodWhichMayFail2:(NSError **)error { // no-warning @@ -36,7 +36,7 @@ struct __CFError {}; typedef struct __CFError* CFErrorRef; void foo(CFErrorRef* error) { // expected-warning {{Function accepting CFErrorRef* should have a non-void return value to indicate whether or not an error occurred}} - *error = 0; // expected-warning {{Potential null dereference.}} + *error = 0; // expected-warning {{Potential null dereference}} } int f1(CFErrorRef* error) { diff --git a/test/Analysis/array-struct.c b/test/Analysis/array-struct.c index c5bdb86a14ed..1b36190729b5 100644 --- a/test/Analysis/array-struct.c +++ b/test/Analysis/array-struct.c @@ -151,7 +151,7 @@ struct s3 p[1]; // an ElementRegion of type 'char'. Then load a nonloc::SymbolVal from it and // assigns to 'a'. void f16(struct s3 *p) { - struct s3 a = *((struct s3*) ((char*) &p[0])); // expected-warning{{Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption.}} + struct s3 a = *((struct s3*) ((char*) &p[0])); // expected-warning{{Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption}} } void inv(struct s1 *); diff --git a/test/Analysis/ctor-inlining.mm b/test/Analysis/ctor-inlining.mm index 54d51b46dc5e..fe4781c16e28 100644 --- a/test/Analysis/ctor-inlining.mm +++ b/test/Analysis/ctor-inlining.mm @@ -39,3 +39,51 @@ void testNonPODCopyConstructor() { clang_analyzer_eval(b.x == 42); // expected-warning{{TRUE}} } + +namespace ConstructorVirtualCalls { + class A { + public: + int *out1, *out2, *out3; + + virtual int get() { return 1; } + + A(int *out1) { + *out1 = get(); + } + }; + + class B : public A { + public: + virtual int get() { return 2; } + + B(int *out1, int *out2) : A(out1) { + *out2 = get(); + } + }; + + class C : public B { + public: + virtual int get() { return 3; } + + C(int *out1, int *out2, int *out3) : B(out1, out2) { + *out3 = get(); + } + }; + + void test() { + int a, b, c; + + C obj(&a, &b, &c); + clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(c == 3); // expected-warning{{TRUE}} + + clang_analyzer_eval(obj.get() == 3); // expected-warning{{TRUE}} + + // Sanity check for devirtualization. + A *base = &obj; + clang_analyzer_eval(base->get() == 3); // expected-warning{{TRUE}} + } +} + + diff --git a/test/Analysis/dtor.cpp b/test/Analysis/dtor.cpp index 620994858c7c..1f45925561c9 100644 --- a/test/Analysis/dtor.cpp +++ b/test/Analysis/dtor.cpp @@ -173,3 +173,57 @@ void testDefaultArg() { // Force a bug to be emitted. *(char *)0 = 1; // expected-warning{{Dereference of null pointer}} } + + +namespace DestructorVirtualCalls { + class A { + public: + int *out1, *out2, *out3; + + virtual int get() { return 1; } + + ~A() { + *out1 = get(); + } + }; + + class B : public A { + public: + virtual int get() { return 2; } + + ~B() { + *out2 = get(); + } + }; + + class C : public B { + public: + virtual int get() { return 3; } + + ~C() { + *out3 = get(); + } + }; + + void test() { + int a, b, c; + + // New scope for the C object. + { + C obj; + clang_analyzer_eval(obj.get() == 3); // expected-warning{{TRUE}} + + // Sanity check for devirtualization. + A *base = &obj; + clang_analyzer_eval(base->get() == 3); // expected-warning{{TRUE}} + + obj.out1 = &a; + obj.out2 = &b; + obj.out3 = &c; + } + + clang_analyzer_eval(a == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(b == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(c == 3); // expected-warning{{TRUE}} + } +} diff --git a/test/Analysis/func.c b/test/Analysis/func.c index b6cebde81091..709ebf779376 100644 --- a/test/Analysis/func.c +++ b/test/Analysis/func.c @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -analyzer-store=region -verify %s + +void clang_analyzer_eval(int); void f(void) { void (*p)(void); @@ -13,3 +15,13 @@ void g(void (*fp)(void)); void f2() { g(f); } + +void f3(void (*f)(void), void (*g)(void)) { + clang_analyzer_eval(!f); // expected-warning{{UNKNOWN}} + f(); + clang_analyzer_eval(!f); // expected-warning{{FALSE}} + + clang_analyzer_eval(!g); // expected-warning{{UNKNOWN}} + (*g)(); + clang_analyzer_eval(!g); // expected-warning{{FALSE}} +} diff --git a/test/Analysis/html-diags.c b/test/Analysis/html-diags.c index b9361f72c64c..7c15df65d750 100644 --- a/test/Analysis/html-diags.c +++ b/test/Analysis/html-diags.c @@ -1,6 +1,6 @@ -// RUN: mkdir %t.dir -// RUN: %clang_cc1 -analyze -analyzer-output=html -analyzer-checker=core -o %t.dir %s -// RUN: rm -fR %t.dir +// RUN: mkdir %T/dir +// RUN: %clang_cc1 -analyze -analyzer-output=html -analyzer-checker=core -o %T/dir %s +// RUN: rm -fR %T/dir // Currently this test mainly checks that the HTML diagnostics doesn't crash // when handling macros will calls with macros. We should actually validate diff --git a/test/Analysis/inline.cpp b/test/Analysis/inline.cpp index 4eaed9fed13c..6b9a885f50f3 100644 --- a/test/Analysis/inline.cpp +++ b/test/Analysis/inline.cpp @@ -166,3 +166,30 @@ namespace PR13569_virtual { x.interface(); } } + +namespace Invalidation { + struct X { + void touch(int &x) const { + x = 0; + } + + void touch2(int &x) const; + + virtual void touchV(int &x) const { + x = 0; + } + + virtual void touchV2(int &x) const; + + int test() const { + // We were accidentally not invalidating under -analyzer-ipa=inlining + // at one point for virtual methods with visible definitions. + int a, b, c, d; + touch(a); + touch2(b); + touchV(c); + touchV2(d); + return a + b + c + d; // no-warning + } + }; +} diff --git a/test/Analysis/inlining/DynDispatchBifurcate.m b/test/Analysis/inlining/DynDispatchBifurcate.m index e78b90bb8c1f..6637dfdba571 100644 --- a/test/Analysis/inlining/DynDispatchBifurcate.m +++ b/test/Analysis/inlining/DynDispatchBifurcate.m @@ -160,17 +160,17 @@ int testCallToPublicAPICat(PublicSubClass *p) { // weither they are "public" or private. int testPublicProperty(PublicClass *p) { int x = 0; - [p setValue3:0]; - if ([p value3] != 0) - return 5/x; // expected-warning {{Division by zero}} // TODO: no warning, we should always inline the property. - return 5/[p value3];// expected-warning {{Division by zero}} + p.value3 = 0; + if (p.value3 != 0) + return 5/x; + return 5/p.value3;// expected-warning {{Division by zero}} } int testExtension(PublicClass *p) { int x = 0; [p setValue2:0]; if ([p value2] != 0) - return 5/x; // expected-warning {{Division by zero}} // TODO: no warning, we should always inline the property. + return 5/x; // expected-warning {{Division by zero}} return 5/[p value2]; // expected-warning {{Division by zero}} } diff --git a/test/Analysis/inlining/InlineObjCClassMethod.m b/test/Analysis/inlining/InlineObjCClassMethod.m index 7e8b51fe0be0..814d437a52d0 100644 --- a/test/Analysis/inlining/InlineObjCClassMethod.m +++ b/test/Analysis/inlining/InlineObjCClassMethod.m @@ -179,3 +179,33 @@ int foo2() { int y = [MyParentSelf testSelf]; return 5/y; // Should warn here. } + +// TODO: We do not inline 'getNum' in the following case, where the value of +// 'self' in call '[self getNum]' is available and evaualtes to +// 'SelfUsedInParentChild' if it's called from fooA. +// Self region should get created before we call foo and yje call to super +// should keep it live. +@interface SelfUsedInParent : NSObject ++ (int)getNum; ++ (int)foo; +@end +@implementation SelfUsedInParent ++ (int)getNum {return 5;} ++ (int)foo { + return [self getNum]; +} +@end +@interface SelfUsedInParentChild : SelfUsedInParent ++ (int)getNum; ++ (int)fooA; +@end +@implementation SelfUsedInParentChild ++ (int)getNum {return 0;} ++ (int)fooA { + return [super foo]; +} +@end +int checkSelfUsedInparentClassMethod() { + return 5/[SelfUsedInParentChild fooA]; +} + diff --git a/test/Analysis/inlining/dyn-dispatch-bifurcate.cpp b/test/Analysis/inlining/dyn-dispatch-bifurcate.cpp new file mode 100644 index 000000000000..fa473aebce32 --- /dev/null +++ b/test/Analysis/inlining/dyn-dispatch-bifurcate.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=dynamic-bifurcate -verify %s + +void clang_analyzer_eval(bool); + +class A { +public: + virtual int get() { return 0; } +}; + +void testBifurcation(A *a) { + clang_analyzer_eval(a->get() == 0); // expected-warning{{TRUE}} expected-warning{{UNKNOWN}} +} + +void testKnown() { + A a; + clang_analyzer_eval(a.get() == 0); // expected-warning{{TRUE}} +} diff --git a/test/Analysis/keychainAPI.m b/test/Analysis/keychainAPI.m index cb4f72c9c454..585a32ddc342 100644 --- a/test/Analysis/keychainAPI.m +++ b/test/Analysis/keychainAPI.m @@ -76,7 +76,7 @@ void errRetVal() { UInt32 length; void *outData; st = SecKeychainItemCopyContent(2, ptr, ptr, &length, &outData); - if (st == GenericError) // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'.}} + if (st == GenericError) // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'}} SecKeychainItemFreeContent(ptr, outData); // expected-warning{{Only call free if a valid (non-NULL) buffer was returned}} } @@ -220,7 +220,7 @@ int foo(CFTypeRef keychainOrArray, SecProtocolType protocol, if (st == noErr) SecKeychainItemFreeContent(ptr, outData[3]); } - if (length) { // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'.}} + if (length) { // expected-warning{{Allocated data is not released: missing a call to 'SecKeychainItemFreeContent'}} length++; } return 0; @@ -318,7 +318,7 @@ void radar10508828_2() { UInt32 pwdLen = 0; void* pwdBytes = 0; OSStatus rc = SecKeychainFindGenericPassword(0, 3, "foo", 3, "bar", &pwdLen, &pwdBytes, 0); - SecKeychainItemFreeContent(0, pwdBytes); // expected-warning {{Only call free if a valid (non-NULL) buffer was returned.}} + SecKeychainItemFreeContent(0, pwdBytes); // expected-warning {{Only call free if a valid (non-NULL) buffer was returned}} } //Example from bug 10797. diff --git a/test/Analysis/malloc-annotations.c b/test/Analysis/malloc-annotations.c index 1dc0f7837bad..9c040b688d2b 100644 --- a/test/Analysis/malloc-annotations.c +++ b/test/Analysis/malloc-annotations.c @@ -208,11 +208,11 @@ void f7_realloc() { } void PR6123() { - int *x = malloc(11); // expected-warning{{Cast a region whose size is not a multiple of the destination type size.}} + int *x = malloc(11); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}} } void PR7217() { - int *buf = malloc(2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size.}} + int *buf = malloc(2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}} buf[1] = 'c'; // not crash } diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c index f60271f39f4c..e3d92d9ad671 100644 --- a/test/Analysis/malloc.c +++ b/test/Analysis/malloc.c @@ -260,11 +260,11 @@ void f7_realloc() { } void PR6123() { - int *x = malloc(11); // expected-warning{{Cast a region whose size is not a multiple of the destination type size.}} + int *x = malloc(11); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}} } void PR7217() { - int *buf = malloc(2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size.}} + int *buf = malloc(2); // expected-warning{{Cast a region whose size is not a multiple of the destination type size}} buf[1] = 'c'; // not crash } @@ -389,7 +389,7 @@ void mallocEscapeMalloc() { void mallocMalloc() { int *p = malloc(12); - p = malloc(12); // expected-warning 2 {{Memory is never released; potential leak}} + p = malloc(12); // expected-warning {{Memory is never released; potential leak}} } void mallocFreeMalloc() { diff --git a/test/Analysis/method-call-path-notes.cpp b/test/Analysis/method-call-path-notes.cpp index fbf0cae7d8a6..17034b9b0001 100644 --- a/test/Analysis/method-call-path-notes.cpp +++ b/test/Analysis/method-call-path-notes.cpp @@ -36,6 +36,11 @@ void test_ic_member_ptr() { (p->*bar)(); // expected-warning {{Called C++ object pointer is null}} expected-note{{Called C++ object pointer is null}} } +void test_cast(const TestInstanceCall *p) { + if (!p) // expected-note {{Assuming pointer value is null}} expected-note {{Taking true branch}} + const_cast<TestInstanceCall *>(p)->foo(); // expected-warning {{Called C++ object pointer is null}} expected-note {{Called C++ object pointer is null}} +} + // CHECK: <?xml version="1.0" encoding="UTF-8"?> // CHECK: <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> // CHECK: <plist version="1.0"> @@ -659,6 +664,86 @@ void test_ic_member_ptr() { // CHECK: <key>file</key><integer>0</integer> // CHECK: </dict> // CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>path</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>kind</key><string>control</string> +// CHECK: <key>edges</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>start</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>line</key><integer>40</integer> +// CHECK: <key>col</key><integer>3</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>line</key><integer>40</integer> +// CHECK: <key>col</key><integer>4</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </array> +// CHECK: <key>end</key> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>line</key><integer>41</integer> +// CHECK: <key>col</key><integer>5</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>line</key><integer>41</integer> +// CHECK: <key>col</key><integer>14</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </array> +// CHECK: </dict> +// CHECK: </array> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>kind</key><string>event</string> +// CHECK: <key>location</key> +// CHECK: <dict> +// CHECK: <key>line</key><integer>41</integer> +// CHECK: <key>col</key><integer>5</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <key>ranges</key> +// CHECK: <array> +// CHECK: <array> +// CHECK: <dict> +// CHECK: <key>line</key><integer>41</integer> +// CHECK: <key>col</key><integer>5</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: <dict> +// CHECK: <key>line</key><integer>41</integer> +// CHECK: <key>col</key><integer>37</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </array> +// CHECK: </array> +// CHECK: <key>depth</key><integer>0</integer> +// CHECK: <key>extended_message</key> +// CHECK: <string>Called C++ object pointer is null</string> +// CHECK: <key>message</key> +// CHECK: <string>Called C++ object pointer is null</string> +// CHECK: </dict> +// CHECK: </array> +// CHECK: <key>description</key><string>Called C++ object pointer is null</string> +// CHECK: <key>category</key><string>Logic error</string> +// CHECK: <key>type</key><string>Called C++ object pointer is null</string> +// CHECK: <key>issue_context_kind</key><string>function</string> +// CHECK: <key>issue_context</key><string>test_cast</string> +// CHECK: <key>issue_hash</key><integer>2</integer> +// CHECK: <key>location</key> +// CHECK: <dict> +// CHECK: <key>line</key><integer>41</integer> +// CHECK: <key>col</key><integer>5</integer> +// CHECK: <key>file</key><integer>0</integer> +// CHECK: </dict> +// CHECK: </dict> // CHECK: </array> // CHECK: </dict> // CHECK: </plist> diff --git a/test/Analysis/method-call.cpp b/test/Analysis/method-call.cpp index 91da532456d7..912062739c34 100644 --- a/test/Analysis/method-call.cpp +++ b/test/Analysis/method-call.cpp @@ -1,25 +1,37 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=inlining -analyzer-store region -verify %s -// XFAIL: * void clang_analyzer_eval(bool); + struct A { int x; A(int a) { x = a; } int getx() const { return x; } }; +void testNullObject(A *a) { + clang_analyzer_eval(a); // expected-warning{{UNKNOWN}} + (void)a->getx(); // assume we know what we're doing + clang_analyzer_eval(a); // expected-warning{{TRUE}} +} + + +// FIXME: These require constructor inlining to be enabled. + void f1() { A x(3); - clang_analyzer_eval(x.getx() == 3); // expected-warning{{TRUE}} + // should be TRUE + clang_analyzer_eval(x.getx() == 3); // expected-warning{{UNKNOWN}} } void f2() { const A &x = A(3); - clang_analyzer_eval(x.getx() == 3); // expected-warning{{TRUE}} + // should be TRUE + clang_analyzer_eval(x.getx() == 3); // expected-warning{{UNKNOWN}} } void f3() { const A &x = (A)3; - clang_analyzer_eval(x.getx() == 3); // expected-warning{{TRUE}} + // should be TRUE + clang_analyzer_eval(x.getx() == 3); // expected-warning{{UNKNOWN}} } diff --git a/test/Analysis/misc-ps-region-store.m b/test/Analysis/misc-ps-region-store.m index 88860bbe10d3..2b481c4a558b 100644 --- a/test/Analysis/misc-ps-region-store.m +++ b/test/Analysis/misc-ps-region-store.m @@ -299,7 +299,7 @@ void test_handle_array_wrapper_helper(); int test_handle_array_wrapper() { struct ArrayWrapper x; test_handle_array_wrapper_helper(&x); - struct WrappedStruct *p = (struct WrappedStruct*) x.y; // expected-warning{{Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption.}} + struct WrappedStruct *p = (struct WrappedStruct*) x.y; // expected-warning{{Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption}} return p->z; // no-warning } diff --git a/test/Analysis/nil-receiver-undefined-larger-than-voidptr-ret.m b/test/Analysis/nil-receiver-undefined-larger-than-voidptr-ret.m index e4d5aaf82b2d..7cf2aee35fc0 100644 --- a/test/Analysis/nil-receiver-undefined-larger-than-voidptr-ret.m +++ b/test/Analysis/nil-receiver-undefined-larger-than-voidptr-ret.m @@ -80,11 +80,11 @@ int handleVoidInComma() { int marker(void) { // control reaches end of non-void function } +// CHECK-darwin8: warning: The receiver of message 'longDoubleM' is nil and returns a value of type 'long double' that will be garbage // CHECK-darwin8: warning: The receiver of message 'longlongM' is nil and returns a value of type 'long long' that will be garbage -// CHECK-darwin8: warning: The receiver of message 'unsignedLongLongM' is nil and returns a value of type 'unsigned long long' that will be garbage // CHECK-darwin8: warning: The receiver of message 'doubleM' is nil and returns a value of type 'double' that will be garbage +// CHECK-darwin8: warning: The receiver of message 'unsignedLongLongM' is nil and returns a value of type 'unsigned long long' that will be garbage // CHECK-darwin8: warning: The receiver of message 'longlongM' is nil and returns a value of type 'long long' that will be garbage -// CHECK-darwin8: warning: The receiver of message 'longDoubleM' is nil and returns a value of type 'long double' that will be garbage // CHECK-darwin9-NOT: warning: The receiver of message 'longlongM' is nil and returns a value of type 'long long' that will be garbage // CHECK-darwin9-NOT: warning: The receiver of message 'unsignedLongLongM' is nil and returns a value of type 'unsigned long long' that will be garbage diff --git a/test/Analysis/ptr-arith.c b/test/Analysis/ptr-arith.c index 6567000c735f..884ae5b9bbcd 100644 --- a/test/Analysis/ptr-arith.c +++ b/test/Analysis/ptr-arith.c @@ -36,7 +36,7 @@ domain_port (const char *domain_b, const char *domain_e, void f3() { int x, y; - int d = &y - &x; // expected-warning{{Subtraction of two pointers that do not point to the same memory chunk may cause incorrect result.}} + int d = &y - &x; // expected-warning{{Subtraction of two pointers that do not point to the same memory chunk may cause incorrect result}} int a[10]; int *p = &a[2]; @@ -46,13 +46,13 @@ void f3() { void f4() { int *p; - p = (int*) 0x10000; // expected-warning{{Using a fixed address is not portable because that address will probably not be valid in all environments or platforms.}} + p = (int*) 0x10000; // expected-warning{{Using a fixed address is not portable because that address will probably not be valid in all environments or platforms}} } void f5() { int x, y; int *p; - p = &x + 1; // expected-warning{{Pointer arithmetic done on non-array variables means reliance on memory layout, which is dangerous.}} + p = &x + 1; // expected-warning{{Pointer arithmetic done on non-array variables means reliance on memory layout, which is dangerous}} int a[10]; p = a + 1; // no-warning diff --git a/test/Analysis/reinterpret-cast.cpp b/test/Analysis/reinterpret-cast.cpp new file mode 100644 index 000000000000..73f2e2de7381 --- /dev/null +++ b/test/Analysis/reinterpret-cast.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-ipa=inlining -verify %s + +void clang_analyzer_eval(bool); + +typedef struct Opaque *Data; +struct IntWrapper { + int x; +}; + +struct Child : public IntWrapper { + void set() { x = 42; } +}; + +void test(Data data) { + Child *wrapper = reinterpret_cast<Child*>(data); + // Don't crash when upcasting here. + // We don't actually know if 'data' is a Child. + wrapper->set(); + clang_analyzer_eval(wrapper->x == 42); // expected-warning{{TRUE}} +} diff --git a/test/Analysis/security-syntax-checks.m b/test/Analysis/security-syntax-checks.m index f4ccefe58cdb..1df8a408a697 100644 --- a/test/Analysis/security-syntax-checks.m +++ b/test/Analysis/security-syntax-checks.m @@ -46,7 +46,7 @@ int getpw(unsigned int uid, char *buf); void test_getpw() { char buff[1024]; - getpw(2, buff); // expected-warning{{The getpw() function is dangerous as it may overflow the provided buffer. It is obsoleted by getpwuid().}} + getpw(2, buff); // expected-warning{{The getpw() function is dangerous as it may overflow the provided buffer. It is obsoleted by getpwuid()}} } // <rdar://problem/6337132> CWE-273: Failure to Check Whether Privileges Were @@ -138,7 +138,7 @@ void test_strcpy() { char x[4]; char *y; - strcpy(x, y); //expected-warning{{Call to function 'strcpy' is insecure as it does not provide bounding of the memory buffer. Replace unbounded copy functions with analogous functions that support length arguments such as 'strlcpy'. CWE-119.}} + strcpy(x, y); //expected-warning{{Call to function 'strcpy' is insecure as it does not provide bounding of the memory buffer. Replace unbounded copy functions with analogous functions that support length arguments such as 'strlcpy'. CWE-119}} } //===----------------------------------------------------------------------=== @@ -162,7 +162,7 @@ void test_strcat() { char x[4]; char *y; - strcat(x, y); //expected-warning{{Call to function 'strcat' is insecure as it does not provide bounding of the memory buffer. Replace unbounded copy functions with analogous functions that support length arguments such as 'strlcat'. CWE-119.}} + strcat(x, y); //expected-warning{{Call to function 'strcat' is insecure as it does not provide bounding of the memory buffer. Replace unbounded copy functions with analogous functions that support length arguments such as 'strlcat'. CWE-119}} } //===----------------------------------------------------------------------=== @@ -173,7 +173,7 @@ typedef __int32_t pid_t; pid_t vfork(void); void test_vfork() { - vfork(); //expected-warning{{Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process.}} + vfork(); //expected-warning{{Call to function 'vfork' is insecure as it can lead to denial of service situations in the parent process}} } //===----------------------------------------------------------------------=== diff --git a/test/Analysis/sizeofpointer.c b/test/Analysis/sizeofpointer.c index 0c86de88ae28..aa85fc002aa1 100644 --- a/test/Analysis/sizeofpointer.c +++ b/test/Analysis/sizeofpointer.c @@ -4,5 +4,5 @@ struct s { }; int f(struct s *p) { - return sizeof(p); // expected-warning{{The code calls sizeof() on a pointer type. This can produce an unexpected result.}} + return sizeof(p); // expected-warning{{The code calls sizeof() on a pointer type. This can produce an unexpected result}} } diff --git a/test/Analysis/stack-addr-ps.cpp b/test/Analysis/stack-addr-ps.cpp index b21a03dc38a4..cbdb143c1857 100644 --- a/test/Analysis/stack-addr-ps.cpp +++ b/test/Analysis/stack-addr-ps.cpp @@ -87,6 +87,6 @@ struct TS { // rdar://11345441 int* f5() { - int& i = i; // expected-warning {{Assigned value is garbage or undefined}} expected-note {{binding reference variable 'i' here}} + int& i = i; // expected-warning {{Assigned value is garbage or undefined}} expected-note {{binding reference variable 'i' here}} expected-warning{{variable 'i' is uninitialized when used within its own initialization}} return &i; // expected-warning {{address of stack memory associated with local variable 'i' returned}} } diff --git a/test/Analysis/stream.c b/test/Analysis/stream.c index e68835e5cfc4..4a095cffd028 100644 --- a/test/Analysis/stream.c +++ b/test/Analysis/stream.c @@ -16,25 +16,25 @@ extern void rewind (FILE *__stream); void f1(void) { FILE *p = fopen("foo", "r"); char buf[1024]; - fread(buf, 1, 1, p); // expected-warning {{Stream pointer might be NULL.}} + fread(buf, 1, 1, p); // expected-warning {{Stream pointer might be NULL}} fclose(p); } void f2(void) { FILE *p = fopen("foo", "r"); - fseek(p, 1, SEEK_SET); // expected-warning {{Stream pointer might be NULL.}} + fseek(p, 1, SEEK_SET); // expected-warning {{Stream pointer might be NULL}} fclose(p); } void f3(void) { FILE *p = fopen("foo", "r"); - ftell(p); // expected-warning {{Stream pointer might be NULL.}} + ftell(p); // expected-warning {{Stream pointer might be NULL}} fclose(p); } void f4(void) { FILE *p = fopen("foo", "r"); - rewind(p); // expected-warning {{Stream pointer might be NULL.}} + rewind(p); // expected-warning {{Stream pointer might be NULL}} fclose(p); } @@ -43,26 +43,26 @@ void f5(void) { if (!p) return; fseek(p, 1, SEEK_SET); // no-warning - fseek(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR.}} + fseek(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR}} fclose(p); } void f6(void) { FILE *p = fopen("foo", "r"); fclose(p); - fclose(p); // expected-warning {{Try to close a file Descriptor already closed. Cause undefined behaviour.}} + fclose(p); // expected-warning {{Try to close a file Descriptor already closed. Cause undefined behaviour}} } void f7(void) { FILE *p = tmpfile(); - ftell(p); // expected-warning {{Stream pointer might be NULL.}} + ftell(p); // expected-warning {{Stream pointer might be NULL}} fclose(p); } void f8(int c) { FILE *p = fopen("foo.c", "r"); if(c) - return; // expected-warning {{Opened File never closed. Potential Resource leak.}} + return; // expected-warning {{Opened File never closed. Potential Resource leak}} fclose(p); } diff --git a/test/Analysis/variadic-method-types.m b/test/Analysis/variadic-method-types.m index 4d0f6bc3f75a..9f90e5ff343d 100644 --- a/test/Analysis/variadic-method-types.m +++ b/test/Analysis/variadic-method-types.m @@ -74,7 +74,7 @@ void f(id a, id<P> b, C* c, C<P> *d, FooType fooType, BarType barType) { [NSArray arrayWithObjects:@"Hello", a, b, c, d, nil]; [NSArray arrayWithObjects:@"Foo", ^{}, nil]; - [NSArray arrayWithObjects:@"Foo", "Bar", "Baz", nil]; // expected-warning 2 {{Argument to 'NSArray' method 'arrayWithObjects:' should be an Objective-C pointer type, not 'char *'}} + [NSArray arrayWithObjects:@"Foo", "Bar", "Baz", nil]; // expected-warning {{Argument to 'NSArray' method 'arrayWithObjects:' should be an Objective-C pointer type, not 'char *'}} [NSDictionary dictionaryWithObjectsAndKeys:@"Foo", "Bar", nil]; // expected-warning {{Argument to 'NSDictionary' method 'dictionaryWithObjectsAndKeys:' should be an Objective-C pointer type, not 'char *'}} [NSSet setWithObjects:@"Foo", "Bar", nil]; // expected-warning {{Argument to 'NSSet' method 'setWithObjects:' should be an Objective-C pointer type, not 'char *'}} [NSOrderedSet orderedSetWithObjects:@"Foo", "Bar", nil]; // expected-warning {{Argument to 'NSOrderedSet' method 'orderedSetWithObjects:' should be an Objective-C pointer type, not 'char *'}} diff --git a/test/CodeCompletion/objc-expr.m b/test/CodeCompletion/objc-expr.m index b59586ab96cf..d3c95a6e6ff1 100644 --- a/test/CodeCompletion/objc-expr.m +++ b/test/CodeCompletion/objc-expr.m @@ -11,7 +11,7 @@ id testCompleteAfterAtSign() { // CHECK-AT: COMPLETION: Pattern : [#char[]#]encode(<#type-name#>) // CHECK-AT: COMPLETION: Pattern : [#Protocol *#]protocol(<#protocol-name#>) // CHECK-AT: COMPLETION: Pattern : [#SEL#]selector(<#selector#>) -// CHECK-AT: COMPLETION: Pattern : [#NSDictionary *#]{<#key#> : <#object, ...#>} +// CHECK-AT: COMPLETION: Pattern : [#NSDictionary *#]{<#key#>: <#object, ...#>} // RUN: %clang_cc1 -fsyntax-only -code-completion-patterns -code-completion-at=%s:4:11 %s -fconst-strings -o - | FileCheck -check-prefix=CONST-STRINGS %s // CHECK-CONST-STRINGS: COMPLETION: Pattern : [#const char[]#]encode(<#type-name#>) diff --git a/test/CodeGen/align-global-large.c b/test/CodeGen/align-global-large.c new file mode 100644 index 000000000000..fcbe758a82e4 --- /dev/null +++ b/test/CodeGen/align-global-large.c @@ -0,0 +1,18 @@ +// PR13606 - Clang crashes with large alignment attribute +// RUN: %clang -S -emit-llvm %s -o - | FileCheck %s + +// CHECK: x +// CHECK: align +// CHECK: 1048576 +volatile char x[4000] __attribute__((aligned(0x100000))); + +int +main (int argc, char ** argv) { + // CHECK: y + // CHECK: align + // CHECK: 1048576 + volatile char y[4000] __attribute__((aligned(0x100000))); + + return y[argc]; +} + diff --git a/test/CodeGen/alignment.c b/test/CodeGen/alignment.c index 8882c91d03e1..98ea01be0920 100644 --- a/test/CodeGen/alignment.c +++ b/test/CodeGen/alignment.c @@ -43,7 +43,8 @@ void test3(packedfloat3 *p) { *p = (packedfloat3) { 3.2f, 2.3f, 0.1f }; } // CHECK: @test3( -// CHECK: store <3 x float> {{.*}}, align 4 +// CHECK: %{{.*}} = bitcast <3 x float>* %{{.*}} to <4 x float>* +// CHECK: store <4 x float> {{.*}}, align 4 // CHECK: ret void diff --git a/test/CodeGen/arm-neon-misc.c b/test/CodeGen/arm-neon-misc.c new file mode 100644 index 000000000000..56ce316c749b --- /dev/null +++ b/test/CodeGen/arm-neon-misc.c @@ -0,0 +1,34 @@ +// REQUIRES: arm-registered-target +// RUN: %clang_cc1 -triple thumbv7-apple-darwin \ +// RUN: -target-abi apcs-gnu \ +// RUN: -target-cpu cortex-a8 \ +// RUN: -mfloat-abi soft \ +// RUN: -target-feature +soft-float-abi \ +// RUN: -ffreestanding \ +// RUN: -emit-llvm -w -o - %s | FileCheck %s + +#include <arm_neon.h> + +// Radar 11998303: Avoid using i64 types for vld1q_lane and vst1q_lane Neon +// intrinsics with <2 x i64> vectors to avoid poor code for i64 in the backend. +void t1(uint64_t *src, uint8_t *dst) { +// CHECK: @t1 + uint64x2_t q = vld1q_u64(src); +// CHECK: call <2 x i64> @llvm.arm.neon.vld1.v2i64 + vst1q_lane_u64(dst, q, 1); +// CHECK: bitcast <16 x i8> %{{.*}} to <2 x i64> +// CHECK: shufflevector <2 x i64> +// CHECK: call void @llvm.arm.neon.vst1.v1i64 +} + +void t2(uint64_t *src1, uint8_t *src2, uint64x2_t *dst) { +// CHECK: @t2 + uint64x2_t q = vld1q_u64(src1); +// CHECK: call <2 x i64> @llvm.arm.neon.vld1.v2i64 + q = vld1q_lane_u64(src2, q, 0); +// CHECK: shufflevector <2 x i64> +// CHECK: call <1 x i64> @llvm.arm.neon.vld1.v1i64 +// CHECK: shufflevector <1 x i64> + *dst = q; +// CHECK: store <2 x i64> +} diff --git a/test/CodeGen/complex-builtints.c b/test/CodeGen/complex-builtints.c new file mode 100644 index 000000000000..09219cfb9d4e --- /dev/null +++ b/test/CodeGen/complex-builtints.c @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 %s -O1 -emit-llvm -o - | FileCheck %s +// rdar://8315199 + +/* Test for builtin conj, creal, cimag. */ +/* Origin: Joseph Myers <jsm28@cam.ac.uk> */ + +extern float _Complex conjf (float _Complex); +extern double _Complex conj (double _Complex); +extern long double _Complex conjl (long double _Complex); + +extern float crealf (float _Complex); +extern double creal (double _Complex); +extern long double creall (long double _Complex); + +extern float cimagf (float _Complex); +extern double cimag (double _Complex); +extern long double cimagl (long double _Complex); + +extern void abort (void); +extern void link_error (void); + +int +main () +{ + /* For each type, test both runtime and compile time (constant folding) + optimization. */ + volatile float _Complex fc = 1.0F + 2.0iF; + volatile double _Complex dc = 1.0 + 2.0i; + volatile long double _Complex ldc = 1.0L + 2.0iL; + /* Test floats. */ + if (__builtin_conjf (fc) != 1.0F - 2.0iF) + abort (); + if (__builtin_conjf (1.0F + 2.0iF) != 1.0F - 2.0iF) + link_error (); + if (__builtin_crealf (fc) != 1.0F) + abort (); + if (__builtin_crealf (1.0F + 2.0iF) != 1.0F) + link_error (); + if (__builtin_cimagf (fc) != 2.0F) + abort (); + if (__builtin_cimagf (1.0F + 2.0iF) != 2.0F) + link_error (); + /* Test doubles. */ + if (__builtin_conj (dc) != 1.0 - 2.0i) + abort (); + if (__builtin_conj (1.0 + 2.0i) != 1.0 - 2.0i) + link_error (); + if (__builtin_creal (dc) != 1.0) + abort (); + if (__builtin_creal (1.0 + 2.0i) != 1.0) + link_error (); + if (__builtin_cimag (dc) != 2.0) + abort (); + if (__builtin_cimag (1.0 + 2.0i) != 2.0) + link_error (); + /* Test long doubles. */ + if (__builtin_conjl (ldc) != 1.0L - 2.0iL) + abort (); + if (__builtin_conjl (1.0L + 2.0iL) != 1.0L - 2.0iL) + link_error (); + if (__builtin_creall (ldc) != 1.0L) + abort (); + if (__builtin_creall (1.0L + 2.0iL) != 1.0L) + link_error (); + if (__builtin_cimagl (ldc) != 2.0L) + abort (); + if (__builtin_cimagl (1.0L + 2.0iL) != 2.0L) + link_error (); +} + +// CHECK-NOT: link_error diff --git a/test/CodeGen/ms-inline-asm.c b/test/CodeGen/ms-inline-asm.c index 8c3e5f7c569b..c140d60551d9 100644 --- a/test/CodeGen/ms-inline-asm.c +++ b/test/CodeGen/ms-inline-asm.c @@ -9,7 +9,9 @@ void t1() { void t2() { // CHECK: @t2 -// CHECK: call void asm sideeffect "nop\0Anop\0Anop", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: call void asm sideeffect "nop", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: call void asm sideeffect "nop", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: call void asm sideeffect "nop", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect // CHECK: ret void __asm nop __asm nop @@ -25,7 +27,8 @@ void t3() { void t4(void) { // CHECK: @t4 -// CHECK: call void asm sideeffect "mov ebx, eax\0Amov ecx, ebx", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: call void asm sideeffect "mov ebx, eax", "~{ebx},~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: call void asm sideeffect "mov ecx, ebx", "~{ecx},~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect // CHECK: ret void __asm mov ebx, eax __asm mov ecx, ebx @@ -33,8 +36,85 @@ void t4(void) { void t5(void) { // CHECK: @t5 -// CHECK: call void asm sideeffect "mov ebx, eax\0Amov ecx, ebx", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: call void asm sideeffect "mov ebx, eax\0Amov ecx, ebx", "~{ebx},~{ecx},~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect // CHECK: ret void __asm mov ebx, eax __asm mov ecx, ebx } +void t6(void) { + __asm int 0x2c +// CHECK: t6 +// CHECK: call void asm sideeffect "int 0x2c", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +} + +void* t7(void) { + __asm mov eax, fs:[0x10] +// CHECK: t7 +// CHECK: call void asm sideeffect "mov eax, fs:[0x10]", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +} + +void t8() { + __asm { + int 0x2c ; } asm comments are fun! }{ + } + __asm {} +// CHECK: t8 +// CHECK: call void asm sideeffect "int 0x2c", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: call void asm sideeffect "", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +} +int t9() { + __asm int 3 ; } comments for single-line asm + __asm {} + __asm int 4 + return 10; +// CHECK: t9 +// CHECK: call void asm sideeffect "int 3", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: call void asm sideeffect "", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: call void asm sideeffect "int 4", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: ret i32 10 +} +void t10() { + __asm { + push ebx + mov ebx, 0x07 + pop ebx + } +// CHECK: t10 +// CHECK: call void asm sideeffect "push ebx\0Amov ebx, 0x07\0Apop ebx", "~{ebx},~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +} + +unsigned t11(void) { + unsigned i = 1, j; + __asm { + mov eax, i + mov j, eax + } + return j; +// CHECK: t11 +// CHECK: [[I:%[a-zA-Z0-9]+]] = alloca i32, align 4 +// CHECK: [[J:%[a-zA-Z0-9]+]] = alloca i32, align 4 +// CHECK: store i32 1, i32* [[I]], align 4 +// CHECK: call void asm sideeffect "mov eax, i\0Amov j, eax", "~{dirflag},~{fpsr},~{flags}"() nounwind ia_nsdialect +// CHECK: [[RET:%[a-zA-Z0-9]+]] = load i32* [[J]], align 4 +// CHECK: ret i32 [[RET]] +} + +void t12(void) { + __asm EVEN + __asm ALIGN +} + +void t13(void) { + __asm { + _emit 0x4A + _emit 0x43 + _emit 0x4B + } +} + +void t14(void) { + unsigned arr[10]; + __asm LENGTH arr ; sizeof(arr)/sizeof(arr[0]) + __asm SIZE arr ; sizeof(arr) + __asm TYPE arr ; sizeof(arr[0]) +} diff --git a/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp b/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp index c5a4094a53c3..7ef4864c8367 100644 --- a/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp +++ b/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp @@ -83,3 +83,20 @@ namespace test3 { d.B::~B(); } } + +namespace test4 { + struct Animal { + virtual void eat(); + }; + struct Fish : Animal { + virtual void eat(); + }; + struct Wrapper { + Fish fish; + }; + extern Wrapper *p; + void test() { + // CHECK: call void @_ZN5test44Fish3eatEv + p->fish.eat(); + } +} diff --git a/test/CodeGenObjC/instance-method-metadata.m b/test/CodeGenObjC/instance-method-metadata.m index ec24c287a36d..0d8c0e0eae50 100644 --- a/test/CodeGenObjC/instance-method-metadata.m +++ b/test/CodeGenObjC/instance-method-metadata.m @@ -28,8 +28,8 @@ @end // CHECK: l_OBJC_$_INSTANCE_METHODS_Bar: -// CHECK-NEXT .long 24 -// CHECK-NEXT .long 2 -// CHECK-NEXT .quad L_OBJC_METH_VAR_NAME_ -// CHECK-NEXT .quad L_OBJC_METH_VAR_TYPE_ -// CHECK-NEXT .quad "-[Bar prop]" +// CHECK-NEXT: .long 24 +// CHECK-NEXT: .long 2 +// CHECK-NEXT: .quad L_OBJC_METH_VAR_NAME_ +// CHECK-NEXT: .quad L_OBJC_METH_VAR_TYPE_ +// CHECK-NEXT: .quad "-[Bar prop]" diff --git a/test/CodeGenObjC/ns_consume_null_check.m b/test/CodeGenObjC/ns_consume_null_check.m index e3b60759e91b..a8e5acd57e61 100644 --- a/test/CodeGenObjC/ns_consume_null_check.m +++ b/test/CodeGenObjC/ns_consume_null_check.m @@ -17,7 +17,7 @@ void foo() [x isEqual : obj]; } -// CHECK: [[TMP:%.*]] = alloca i8 +// CHECK: [[TMP:%.*]] = alloca i8{{$}} // CHECK: [[FIVE:%.*]] = call i8* @objc_retain // CHECK-NEXT: [[SIX:%.*]] = bitcast // CHECK-NEXT: [[SEVEN:%.*]] = icmp eq i8* [[SIX]], null @@ -25,8 +25,8 @@ void foo() // CHECK: [[FN:%.*]] = load i8** getelementptr inbounds // CHECK-NEXT: [[EIGHT:%.*]] = bitcast i8* [[FN]] // CHECK-NEXT: [[CALL:%.*]] = call signext i8 [[EIGHT]] -// CHECK-NEXT store i8 [[CALL]], i8* [[TMP]] -// CHECK-NEXT br label [[CONT:%.*]] +// CHECK-NEXT: store i8 [[CALL]], i8* [[TMP]] +// CHECK-NEXT: br label [[CONT:%.*]] // CHECK: call void @objc_release(i8* [[FIVE]]) nounwind // CHECK-NEXT: call void @llvm.memset -// CHECK-NEXT br label [[CONT]] +// CHECK-NEXT: br label [[CONT]] diff --git a/test/CodeGenOpenCL/vectorLoadStore.cl b/test/CodeGenOpenCL/vectorLoadStore.cl new file mode 100644 index 000000000000..44bc7bd25d45 --- /dev/null +++ b/test/CodeGenOpenCL/vectorLoadStore.cl @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 %s -emit-llvm -O0 -o - | FileCheck %s + +typedef char char3 __attribute((ext_vector_type(3)));; + +// Check for optimized vec3 load/store which treats vec3 as vec4. +void foo(char3 *P, char3 *Q) { + *P = *Q; + // CHECK: %{{.*}} = shufflevector <4 x i8> %{{.*}}, <4 x i8> undef, <3 x i32> <i32 0, i32 1, i32 2> +} diff --git a/test/Driver/Xlinker-args.c b/test/Driver/Xlinker-args.c index b4e5a9b08f43..d89d5bad1aaf 100644 --- a/test/Driver/Xlinker-args.c +++ b/test/Driver/Xlinker-args.c @@ -4,6 +4,13 @@ // RUN: %clang -target i386-apple-darwin9 -### \ // RUN: -Xlinker one -Xlinker --no-demangle \ // RUN: -Wl,two,--no-demangle,three -Xlinker four %s 2> %t -// RUN: FileCheck < %t %s +// RUN: FileCheck -check-prefix=DARWIN < %t %s // -// CHECK: "one" "two" "three" "four" +// RUN: %clang -target x86_64-pc-linux-gnu -### \ +// RUN: -Xlinker one -Xlinker --no-demangle \ +// RUN: -Wl,two,--no-demangle,three -Xlinker four %s 2> %t +// RUN: FileCheck -check-prefix=LINUX < %t %s +// +// DARWIN-NOT: --no-demangle +// DARWIN: "one" "two" "three" "four" +// LINUX: "--no-demangle" "one" "two" "three" "four" diff --git a/test/Index/complete-enums.cpp b/test/Index/complete-enums.cpp index 49a893258773..23c60ac4dfa5 100644 --- a/test/Index/complete-enums.cpp +++ b/test/Index/complete-enums.cpp @@ -1,6 +1,6 @@ // Note: the run lines follow their respective tests, since line/column // matter in this test. - +struct X { X(); ~X(); }; enum class Color { Red = 17, Green, @@ -9,7 +9,7 @@ enum class Color { int Greeby(); void f(Color color) { switch (color) { - case Color::Green: + case Color::Green: { X x; } case Color::Red; } } diff --git a/test/Index/complete-exprs.m b/test/Index/complete-exprs.m index 3fb1540351d5..16eeda9bff4b 100644 --- a/test/Index/complete-exprs.m +++ b/test/Index/complete-exprs.m @@ -21,7 +21,7 @@ __strong id global; // CHECK-CC1: NotImplemented:{ResultType NSString *}{TypedText @"}{Placeholder string}{Text "} (40) // CHECK-CC1: NotImplemented:{ResultType id}{TypedText @(}{Placeholder expression}{RightParen )} (40) // CHECK-CC1: NotImplemented:{ResultType NSArray *}{TypedText @[}{Placeholder objects, ...}{RightBracket ]} (40) -// CHECK-CC1: NotImplemented:{ResultType NSDictionary *}{TypedText @{}{Placeholder key}{HorizontalSpace }{Colon :}{HorizontalSpace }{Placeholder object, ...}{RightBrace }} (40) +// CHECK-CC1: NotImplemented:{ResultType NSDictionary *}{TypedText @{}{Placeholder key}{Colon :}{HorizontalSpace }{Placeholder object, ...}{RightBrace }} (40) // CHECK-CC1: NotImplemented:{ResultType SEL}{TypedText _cmd} (80) // CHECK-CC1: TypedefDecl:{TypedText BOOL} (50) // CHECK-CC1: macro definition:{TypedText bool} (51) @@ -43,7 +43,7 @@ __strong id global; // RUN: c-index-test -code-completion-at=%s:16:5 %s | FileCheck -check-prefix=CHECK-CC4 %s // RUN: c-index-test -code-completion-at=%s:16:14 %s | FileCheck -check-prefix=CHECK-CC4 %s // CHECK-CC4: NotImplemented:{ResultType NSArray *}{TypedText @[}{Placeholder objects, ...}{RightBracket ]} (40) -// CHECK-CC4: NotImplemented:{ResultType NSDictionary *}{TypedText @{}{Placeholder key}{HorizontalSpace }{Colon :}{HorizontalSpace }{Placeholder object, ...}{RightBrace }} (40) +// CHECK-CC4: NotImplemented:{ResultType NSDictionary *}{TypedText @{}{Placeholder key}{Colon :}{HorizontalSpace }{Placeholder object, ...}{RightBrace }} (40) // CHECK-CC4: NotImplemented:{ResultType SEL}{TypedText _cmd} (80) // CHECK-CC4: macro definition:{TypedText bool} (51) // CHECK-CC4: macro definition:{TypedText NO} (65) diff --git a/test/Index/complete-preamble.cpp b/test/Index/complete-preamble.cpp new file mode 100644 index 000000000000..8f4810522527 --- /dev/null +++ b/test/Index/complete-preamble.cpp @@ -0,0 +1,8 @@ +#include "complete-preamble.h" +void f() { + std:: +} + +// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:3:8 %s -o - | FileCheck -check-prefix=CC1 %s +// CHECK-CC1: {ResultType void}{TypedText wibble}{LeftParen (}{RightParen )} (50) (parent: Namespace 'std') + diff --git a/test/Index/complete-preamble.h b/test/Index/complete-preamble.h new file mode 100644 index 000000000000..e696284c0964 --- /dev/null +++ b/test/Index/complete-preamble.h @@ -0,0 +1,6 @@ +namespace std { + void wibble(); +} + +namespace std { +} diff --git a/test/Parser/ms-inline-asm.c b/test/Parser/ms-inline-asm.c index 2d181958857b..5326ce4d2417 100644 --- a/test/Parser/ms-inline-asm.c +++ b/test/Parser/ms-inline-asm.c @@ -11,13 +11,13 @@ void t5() { __asm { // expected-warning {{MS-style inline assembly is not supported}} int 0x2c ; } asm comments are fun! }{ } - __asm {} // no warning as this gets merged with the previous inline asm + __asm {} // expected-warning {{MS-style inline assembly is not supported}} } int t6() { __asm int 3 ; } comments for single-line asm // expected-warning {{MS-style inline assembly is not supported}} - __asm {} // no warning as this gets merged with the previous inline asm + __asm {} // expected-warning {{MS-style inline assembly is not supported}} - __asm int 4 // no warning as this gets merged with the previous inline asm + __asm int 4 // expected-warning {{MS-style inline assembly is not supported}} return 10; } int t7() { diff --git a/test/Sema/128bitint.c b/test/Sema/128bitint.c index ddad83554753..600c25a630c2 100644 --- a/test/Sema/128bitint.c +++ b/test/Sema/128bitint.c @@ -18,3 +18,22 @@ long long Signed64 = 123456789012345678901234567890i128; // expected-warning {{i unsigned long long UnsignedTooBig = 123456789012345678901234567890; // expected-warning {{integer constant is too large for its type}} __uint128_t Unsigned128 = 123456789012345678901234567890Ui128; unsigned long long Unsigned64 = 123456789012345678901234567890Ui128; // expected-warning {{implicit conversion from 'unsigned __int128' to 'unsigned long long' changes value from 123456789012345678901234567890 to 14083847773837265618}} + +// Ensure we don't crash when user passes 128-bit values to type safety +// attributes. +void pointer_with_type_tag_arg_num_1(void *buf, int datatype) + __attribute__(( pointer_with_type_tag(mpi,0x10000000000000001i128,1) )); // expected-error {{attribute parameter 2 is out of bounds}} + +void pointer_with_type_tag_arg_num_2(void *buf, int datatype) + __attribute__(( pointer_with_type_tag(mpi,1,0x10000000000000001i128) )); // expected-error {{attribute parameter 3 is out of bounds}} + +void MPI_Send(void *buf, int datatype) __attribute__(( pointer_with_type_tag(mpi,1,2) )); + +static const __uint128_t mpi_int_wrong __attribute__(( type_tag_for_datatype(mpi,int) )) = 0x10000000000000001i128; // expected-error {{'type_tag_for_datatype' attribute requires the initializer to be an integer constant expression that can be represented by a 64 bit integer}} +static const int mpi_int __attribute__(( type_tag_for_datatype(mpi,int) )) = 10; + +void test(int *buf) +{ + MPI_Send(buf, 0x10000000000000001i128); // expected-warning {{implicit conversion from '__int128' to 'int' changes value}} +} + diff --git a/test/Sema/arm-asm.c b/test/Sema/arm-asm.c new file mode 100644 index 000000000000..3fc0eeb7543a --- /dev/null +++ b/test/Sema/arm-asm.c @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 %s -triple armv7-apple-darwin -verify -fsyntax-only + +void f (void) { + int Val; + asm volatile ("lw (r1), %0[val]": "=&b"(Val)); // expected-error {{invalid output constraint '=&b' in asm}} + return; +} diff --git a/test/Sema/builtins-decl.c b/test/Sema/builtins-decl.c index 19bdb840ccfb..d6b004aa8820 100644 --- a/test/Sema/builtins-decl.c +++ b/test/Sema/builtins-decl.c @@ -6,3 +6,8 @@ extern unsigned int __builtin_ia32_crc32qi (unsigned int, unsigned char); extern unsigned int __builtin_ia32_crc32hi (unsigned int, unsigned short); extern unsigned int __builtin_ia32_crc32si (unsigned int, unsigned int); + +// GCC documents these as unsigned, but they are defined with a signed argument. +extern int __builtin_ffs(int); +extern int __builtin_ffsl(long); +extern int __builtin_ffsll(long long); diff --git a/test/Sema/callingconv.c b/test/Sema/callingconv.c index 25669f08ae29..6c844a373318 100644 --- a/test/Sema/callingconv.c +++ b/test/Sema/callingconv.c @@ -36,6 +36,14 @@ void (__attribute__((cdecl)) *pctest2)() = ctest2; typedef void (__attribute__((fastcall)) *Handler) (float *); Handler H = foo; +int __attribute__((pcs("aapcs", "aapcs"))) pcs1(void); // expected-error {{attribute takes one argument}} +int __attribute__((pcs())) pcs2(void); // expected-error {{attribute takes one argument}} +int __attribute__((pcs(pcs1))) pcs3(void); // expected-error {{attribute takes one argument}} +int __attribute__((pcs(0))) pcs4(void); // expected-error {{'pcs' attribute requires parameter 1 to be a string}} +int __attribute__((pcs("aapcs"))) pcs5(void); // no-error +int __attribute__((pcs("aapcs-vfp"))) pcs6(void); // no-error +int __attribute__((pcs("foo"))) pcs7(void); // expected-error {{Invalid PCS type}} + // PR6361 void ctest3(); void __attribute__((cdecl)) ctest3() {} diff --git a/test/Sema/static-array.c b/test/Sema/static-array.c index 2d4b968decda..5ca693b2bf54 100644 --- a/test/Sema/static-array.c +++ b/test/Sema/static-array.c @@ -1,12 +1,9 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s void cat0(int a[static 0]) {} // expected-warning {{'static' has no effect on zero-length arrays}} void cat(int a[static 3]) {} // expected-note 2 {{callee declares array parameter as static here}} -typedef int i3[static 3]; -void tcat(i3 a) {} - void vat(int i, int a[static i]) {} // expected-note {{callee declares array parameter as static here}} void f(int *p) { @@ -20,12 +17,41 @@ void f(int *p) { cat(c); cat(p); - tcat(0); // expected-warning {{null passed to a callee which requires a non-null argument}} - tcat(a); // expected-warning {{array argument is too small; contains 2 elements, callee requires at least 3}} - tcat(b); - tcat(c); - tcat(p); - vat(1, 0); // expected-warning {{null passed to a callee which requires a non-null argument}} vat(3, b); } + + +typedef int td[static 3]; // expected-error {{'static' used in array declarator outside of function prototype}} +typedef void(*fp)(int[static 42]); // no-warning + +void g(void) { + int a[static 42]; // expected-error {{'static' used in array declarator outside of function prototype}} + + int b[const 10]; // expected-error {{type qualifier used in array declarator outside of function prototype}} + int c[volatile 10]; // expected-error {{type qualifier used in array declarator outside of function prototype}} + int d[restrict 10]; // expected-error {{type qualifier used in array declarator outside of function prototype}} + + int e[static restrict 1]; // expected-error {{'static' used in array declarator outside of function prototype}} +} + +void h(int [static const 10][42]); // no-warning + +void i(int [10] + [static 42]); // expected-error {{'static' used in non-outermost array type derivation}} + +void j(int [10] + [const 42]); // expected-error {{type qualifier used in non-outermost array type derivation}} + +void k(int (*x)[static 10]); // expected-error {{'static' used in non-outermost array type derivation}} +void l(int (x)[static 10]); // no-warning +void m(int *x[static 10]); // no-warning +void n(int *(x)[static 10]); // no-warning + +void o(int (x[static 10])(void)); // expected-error{{'x' declared as array of functions of type 'int (void)'}} +void p(int (^x)[static 10]); // expected-error{{block pointer to non-function type is invalid}} +void q(int (^x[static 10])()); // no-warning + +void r(x) + int x[restrict]; // no-warning +{} diff --git a/test/Sema/tentative-decls.c b/test/Sema/tentative-decls.c index b15537bfa0cd..e14540ba8417 100644 --- a/test/Sema/tentative-decls.c +++ b/test/Sema/tentative-decls.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -fsyntax-only -verify +// RUN: %clang_cc1 %s -fsyntax-only -Wprivate-extern -verify // PR3310 struct a x1; // expected-note 2{{forward declaration of 'struct a'}} @@ -32,7 +32,8 @@ int i2 = 3; // expected-error{{non-static declaration of 'i2' follows static dec static int i3 = 5; extern int i3; -__private_extern__ int pExtern; +// rdar://7703982 +__private_extern__ int pExtern; // expected-warning {{Use of __private_extern__ on tentative definition has unexpected behaviour}} int pExtern = 0; int i4; diff --git a/test/Sema/warn-documentation.cpp b/test/Sema/warn-documentation.cpp index 16ba4297cdc4..d99520b6733e 100644 --- a/test/Sema/warn-documentation.cpp +++ b/test/Sema/warn-documentation.cpp @@ -624,13 +624,9 @@ class test_attach37 { /// \brief\author Aaa /// \tparam T Aaa void test_attach38(int aaa, int bbb); -}; -// expected-warning@+1 {{empty paragraph passed to '\brief' command}} -/// \brief\author Aaa -/// \tparam T Aaa -template<typename T> -void test_attach37<T>::test_attach38(int aaa, int bbb) {} + void test_attach39(int aaa, int bbb); +}; // expected-warning@+2 {{empty paragraph passed to '\brief' command}} // expected-warning@+2 {{template parameter 'T' not found in the template declaration}} @@ -639,6 +635,29 @@ void test_attach37<T>::test_attach38(int aaa, int bbb) {} template<> void test_attach37<int>::test_attach38(int aaa, int bbb) {} +// expected-warning@+1 {{empty paragraph passed to '\brief' command}} +/// \brief\author Aaa +/// \tparam T Aaa +template<typename T> +void test_attach37<T>::test_attach39(int aaa, int bbb) {} + +// We used to emit warning that parameter 'a' is not found because we parsed +// the comment in context of the redeclaration which does not have parameter +// names. +template <typename T> +struct test_attach38 { + /*! + \param a First param + \param b Second param + */ + template <typename B> + void test_attach39(T a, B b); +}; + +template <> +template <typename B> +void test_attach38<int>::test_attach39(int, B); + // PR13411, reduced. We used to crash on this. /** @@ -652,7 +671,7 @@ void test_nocrash1(int); /// \param\brief void test_nocrash2(int); -// PR13593 +// PR13593, example 1 and 2 /** * Bla. @@ -668,3 +687,30 @@ template <typename> void test_nocrash3() { } + +// PR13593, example 3 + +/** + * aaa + */ +template <typename T> +inline T test_nocrash5(T a1) +{ + return a1; +} + +/// +//, + +inline void test_nocrash6() +{ + test_nocrash5(1); +} + +// We used to crash on this. + +/*! + Blah. +*/ +typedef const struct test_nocrash7 * test_nocrash8; + diff --git a/test/Sema/warn-type-safety-mpi-hdf5.c b/test/Sema/warn-type-safety-mpi-hdf5.c new file mode 100644 index 000000000000..9c2ee965866d --- /dev/null +++ b/test/Sema/warn-type-safety-mpi-hdf5.c @@ -0,0 +1,307 @@ +// RUN: %clang_cc1 -std=c99 -DOPEN_MPI -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c99 -DMPICH -fsyntax-only -verify %s +// RUN: %clang_cc1 -x c++ -std=c++98 -DOPEN_MPI -fsyntax-only -verify %s +// RUN: %clang_cc1 -x c++ -std=c++98 -DMPICH -fsyntax-only -verify %s +// +// RUN: %clang_cc1 -std=c99 -DOPEN_MPI -fno-signed-char -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c99 -DMPICH -fno-signed-char -fsyntax-only -verify %s + +//===--- limits.h mock ----------------------------------------------------===// + +#ifdef __CHAR_UNSIGNED__ +#define CHAR_MIN 0 +#define CHAR_MAX (__SCHAR_MAX__*2 +1) +#else +#define CHAR_MIN (-__SCHAR_MAX__-1) +#define CHAR_MAX __SCHAR_MAX__ +#endif + +//===--- mpi.h mock -------------------------------------------------------===// + +#define NULL ((void *)0) + +#ifdef OPEN_MPI +typedef struct ompi_datatype_t *MPI_Datatype; +#endif + +#ifdef MPICH +typedef int MPI_Datatype; +#endif + +int MPI_Send(void *buf, int count, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,1,3) )); + +int MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype, + void *recvbuf, int recvcount, MPI_Datatype recvtype) + __attribute__(( pointer_with_type_tag(mpi,1,3), pointer_with_type_tag(mpi,4,6) )); + +#ifdef OPEN_MPI +// OpenMPI and LAM/MPI-style datatype definitions + +#define OMPI_PREDEFINED_GLOBAL(type, global) ((type) &(global)) + +#define MPI_DATATYPE_NULL OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_datatype_null) +#define MPI_FLOAT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_float) +#define MPI_INT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_int) +#define MPI_LONG OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_long) +#define MPI_LONG_LONG_INT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_long_long_int) +#define MPI_CHAR OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_char) + +#define MPI_FLOAT_INT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_float_int) +#define MPI_2INT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_2int) + +#define MPI_IN_PLACE ((void *) 1) + +extern struct ompi_predefined_datatype_t ompi_mpi_datatype_null __attribute__(( type_tag_for_datatype(mpi,void,must_be_null) )); +extern struct ompi_predefined_datatype_t ompi_mpi_float __attribute__(( type_tag_for_datatype(mpi,float) )); +extern struct ompi_predefined_datatype_t ompi_mpi_int __attribute__(( type_tag_for_datatype(mpi,int) )); +extern struct ompi_predefined_datatype_t ompi_mpi_long __attribute__(( type_tag_for_datatype(mpi,long) )); +extern struct ompi_predefined_datatype_t ompi_mpi_long_long_int __attribute__(( type_tag_for_datatype(mpi,long long int) )); +extern struct ompi_predefined_datatype_t ompi_mpi_char __attribute__(( type_tag_for_datatype(mpi,char) )); + +struct ompi_struct_mpi_float_int {float f; int i;}; +extern struct ompi_predefined_datatype_t ompi_mpi_float_int __attribute__(( type_tag_for_datatype(mpi, struct ompi_struct_mpi_float_int, layout_compatible) )); + +struct ompi_struct_mpi_2int {int i1; int i2;}; +extern struct ompi_predefined_datatype_t ompi_mpi_2int __attribute__(( type_tag_for_datatype(mpi, struct ompi_struct_mpi_2int, layout_compatible) )); +#endif + +#ifdef MPICH +// MPICH2 and MVAPICH2-style datatype definitions + +#define MPI_COMM_WORLD ((MPI_Comm) 0x44000000) + +#define MPI_DATATYPE_NULL ((MPI_Datatype) 0xa0000000) +#define MPI_FLOAT ((MPI_Datatype) 0xa0000001) +#define MPI_INT ((MPI_Datatype) 0xa0000002) +#define MPI_LONG ((MPI_Datatype) 0xa0000003) +#define MPI_LONG_LONG_INT ((MPI_Datatype) 0xa0000004) +#define MPI_CHAR ((MPI_Datatype) 0xa0000005) + +#define MPI_FLOAT_INT ((MPI_Datatype) 0xa0000006) +#define MPI_2INT ((MPI_Datatype) 0xa0000007) + +#define MPI_IN_PLACE (void *) -1 + +static const MPI_Datatype mpich_mpi_datatype_null __attribute__(( type_tag_for_datatype(mpi,void,must_be_null) )) = 0xa0000000; +static const MPI_Datatype mpich_mpi_float __attribute__(( type_tag_for_datatype(mpi,float) )) = 0xa0000001; +static const MPI_Datatype mpich_mpi_int __attribute__(( type_tag_for_datatype(mpi,int) )) = 0xa0000002; +static const MPI_Datatype mpich_mpi_long __attribute__(( type_tag_for_datatype(mpi,long) )) = 0xa0000003; +static const MPI_Datatype mpich_mpi_long_long_int __attribute__(( type_tag_for_datatype(mpi,long long int) )) = 0xa0000004; +static const MPI_Datatype mpich_mpi_char __attribute__(( type_tag_for_datatype(mpi,char) )) = 0xa0000005; + +struct mpich_struct_mpi_float_int { float f; int i; }; +struct mpich_struct_mpi_2int { int i1; int i2; }; +static const MPI_Datatype mpich_mpi_float_int __attribute__(( type_tag_for_datatype(mpi, struct mpich_struct_mpi_float_int, layout_compatible) )) = 0xa0000006; +static const MPI_Datatype mpich_mpi_2int __attribute__(( type_tag_for_datatype(mpi, struct mpich_struct_mpi_2int, layout_compatible) )) = 0xa0000007; +#endif + +//===--- HDF5 headers mock ------------------------------------------------===// + +typedef int hid_t; +void H5open(void); + +#ifndef HDF_PRIVATE +#define H5OPEN H5open(), +#else +#define H5OPEN +#endif + +#define H5T_NATIVE_CHAR (CHAR_MIN?H5T_NATIVE_SCHAR:H5T_NATIVE_UCHAR) +#define H5T_NATIVE_SCHAR (H5OPEN H5T_NATIVE_SCHAR_g) +#define H5T_NATIVE_UCHAR (H5OPEN H5T_NATIVE_UCHAR_g) +#define H5T_NATIVE_INT (H5OPEN H5T_NATIVE_INT_g) +#define H5T_NATIVE_LONG (H5OPEN H5T_NATIVE_LONG_g) + +hid_t H5T_NATIVE_SCHAR_g __attribute__(( type_tag_for_datatype(hdf5,signed char) )); +hid_t H5T_NATIVE_UCHAR_g __attribute__(( type_tag_for_datatype(hdf5,unsigned char) )); +hid_t H5T_NATIVE_INT_g __attribute__(( type_tag_for_datatype(hdf5,int) )); +hid_t H5T_NATIVE_LONG_g __attribute__(( type_tag_for_datatype(hdf5,long) )); + +void H5Dwrite(hid_t mem_type_id, const void *buf) __attribute__(( pointer_with_type_tag(hdf5,2,1) )); + +//===--- Tests ------------------------------------------------------------===// + +//===--- MPI + +struct pair_float_int +{ + float f; int i; +}; + +struct pair_int_int +{ + int i1; int i2; +}; + +void test_mpi_predefined_types( + int *int_buf, + long *long_buf1, + long *long_buf2, + void *void_buf, + struct pair_float_int *pfi, + struct pair_int_int *pii) +{ + char char_buf[255]; + + // Layout-compatible scalar types. + MPI_Send(int_buf, 1, MPI_INT); // no-warning + + // Layout-compatible class types. + MPI_Send(pfi, 1, MPI_FLOAT_INT); // no-warning + MPI_Send(pii, 1, MPI_2INT); // no-warning + + // Layout-incompatible scalar types. + MPI_Send(long_buf1, 1, MPI_INT); // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag that requires 'int *'}} + + // Layout-incompatible class types. + MPI_Send(pii, 1, MPI_FLOAT_INT); // expected-warning {{argument type 'struct pair_int_int *' doesn't match specified 'mpi' type tag}} + MPI_Send(pfi, 1, MPI_2INT); // expected-warning {{argument type 'struct pair_float_int *' doesn't match specified 'mpi' type tag}} + + // Layout-incompatible class-scalar types. + MPI_Send(long_buf1, 1, MPI_2INT); // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag}} + + // Function with two buffers. + MPI_Gather(long_buf1, 1, MPI_INT, // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag that requires 'int *'}} + long_buf2, 1, MPI_INT); // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag that requires 'int *'}} + + // Array buffers should work like pointer buffers. + MPI_Send(char_buf, 255, MPI_CHAR); // no-warning + + // Explicit casts should not be dropped. + MPI_Send((int *) char_buf, 255, MPI_INT); // no-warning + MPI_Send((int *) char_buf, 255, MPI_CHAR); // expected-warning {{argument type 'int *' doesn't match specified 'mpi' type tag that requires 'char *'}} + + // `void*' buffer should never warn. + MPI_Send(void_buf, 255, MPI_CHAR); // no-warning + + // We expect that MPI_IN_PLACE is `void*', shouldn't warn. + MPI_Gather(MPI_IN_PLACE, 0, MPI_INT, + int_buf, 1, MPI_INT); + + // Special handling for MPI_DATATYPE_NULL: buffer pointer should be either + // a `void*' pointer or a null pointer constant. + MPI_Gather(NULL, 0, MPI_DATATYPE_NULL, // no-warning + int_buf, 1, MPI_INT); + + MPI_Gather(int_buf, 0, MPI_DATATYPE_NULL, // expected-warning {{specified mpi type tag requires a null pointer}} + int_buf, 1, MPI_INT); +} + +MPI_Datatype my_int_datatype __attribute__(( type_tag_for_datatype(mpi,int) )); + +struct S1 { int a; int b; }; +MPI_Datatype my_s1_datatype __attribute__(( type_tag_for_datatype(mpi,struct S1) )); + +// Layout-compatible to S1, but should be treated as a different type. +struct S2 { int a; int b; }; +MPI_Datatype my_s2_datatype __attribute__(( type_tag_for_datatype(mpi,struct S2) )); + +void test_user_types(int *int_buf, + long *long_buf, + struct S1 *s1_buf, + struct S2 *s2_buf) +{ + MPI_Send(int_buf, 1, my_int_datatype); // no-warning + MPI_Send(long_buf, 1, my_int_datatype); // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag that requires 'int *'}} + + MPI_Send(s1_buf, 1, my_s1_datatype); // no-warning + MPI_Send(s1_buf, 1, my_s2_datatype); // expected-warning {{argument type 'struct S1 *' doesn't match specified 'mpi' type tag that requires 'struct S2 *'}} + + MPI_Send(long_buf, 1, my_s1_datatype); // expected-warning {{argument type 'long *' doesn't match specified 'mpi' type tag that requires 'struct S1 *'}} + MPI_Send(s1_buf, 1, MPI_INT); // expected-warning {{argument type 'struct S1 *' doesn't match specified 'mpi' type tag that requires 'int *'}} +} + +MPI_Datatype my_unknown_datatype; + +void test_not_annotated(int *int_buf, + long *long_buf, + MPI_Datatype type) +{ + // Using 'MPI_Datatype's without attributes should not produce warnings. + MPI_Send(long_buf, 1, my_unknown_datatype); // no-warning + MPI_Send(int_buf, 1, type); // no-warning +} + +struct S1_compat { int a; int b; }; +MPI_Datatype my_s1_compat_datatype + __attribute__(( type_tag_for_datatype(mpi, struct S1_compat, layout_compatible) )); + +struct S3 { int a; long b; double c; double d; struct S1 s1; }; +struct S3_compat { int a; long b; double c; double d; struct S2 s2; }; +MPI_Datatype my_s3_compat_datatype + __attribute__(( type_tag_for_datatype(mpi, struct S3_compat, layout_compatible) )); + +struct S4 { char c; }; +struct S4_compat { signed char c; }; +MPI_Datatype my_s4_compat_datatype + __attribute__(( type_tag_for_datatype(mpi, struct S4_compat, layout_compatible) )); + +union U1 { int a; long b; double c; double d; struct S1 s1; }; +union U1_compat { long b; double c; struct S2 s; int a; double d; }; +MPI_Datatype my_u1_compat_datatype + __attribute__(( type_tag_for_datatype(mpi, union U1_compat, layout_compatible) )); + +union U2 { int a; long b; double c; struct S1 s1; }; +MPI_Datatype my_u2_datatype + __attribute__(( type_tag_for_datatype(mpi, union U2, layout_compatible) )); + +void test_layout_compatibility(struct S1 *s1_buf, struct S3 *s3_buf, + struct S4 *s4_buf, + union U1 *u1_buf, union U2 *u2_buf) +{ + MPI_Send(s1_buf, 1, my_s1_compat_datatype); // no-warning + MPI_Send(s3_buf, 1, my_s3_compat_datatype); // no-warning + MPI_Send(s1_buf, 1, my_s3_compat_datatype); // expected-warning {{argument type 'struct S1 *' doesn't match specified 'mpi' type tag}} + MPI_Send(s4_buf, 1, my_s4_compat_datatype); // expected-warning {{argument type 'struct S4 *' doesn't match specified 'mpi' type tag}} + MPI_Send(u1_buf, 1, my_u1_compat_datatype); // no-warning + MPI_Send(u1_buf, 1, my_u2_datatype); // expected-warning {{argument type 'union U1 *' doesn't match specified 'mpi' type tag}} + MPI_Send(u2_buf, 1, my_u1_compat_datatype); // expected-warning {{argument type 'union U2 *' doesn't match specified 'mpi' type tag}} +} + +// There is an MPI_REAL predefined in MPI, but some existing MPI programs do +// this. +typedef float real; +#define MPI_REAL MPI_FLOAT + +void test_mpi_real_user_type(real *real_buf, float *float_buf) +{ + MPI_Send(real_buf, 1, MPI_REAL); // no-warning + MPI_Send(real_buf, 1, MPI_FLOAT); // no-warning + MPI_Send(float_buf, 1, MPI_REAL); // no-warning + MPI_Send(float_buf, 1, MPI_FLOAT); // no-warning +} + +//===--- HDF5 + +void test_hdf5(char *char_buf, + signed char *schar_buf, + unsigned char *uchar_buf, + int *int_buf, + long *long_buf) +{ + H5Dwrite(H5T_NATIVE_CHAR, char_buf); // no-warning +#ifdef __CHAR_UNSIGNED__ + H5Dwrite(H5T_NATIVE_CHAR, schar_buf); // expected-warning {{argument type 'signed char *' doesn't match specified 'hdf5' type tag that requires 'unsigned char *'}} + H5Dwrite(H5T_NATIVE_CHAR, uchar_buf); // no-warning +#else + H5Dwrite(H5T_NATIVE_CHAR, schar_buf); // no-warning + H5Dwrite(H5T_NATIVE_CHAR, uchar_buf); // expected-warning {{argument type 'unsigned char *' doesn't match specified 'hdf5' type tag that requires 'signed char *'}} +#endif + H5Dwrite(H5T_NATIVE_SCHAR, schar_buf); // no-warning + H5Dwrite(H5T_NATIVE_UCHAR, uchar_buf); // no-warning + H5Dwrite(H5T_NATIVE_INT, int_buf); // no-warning + H5Dwrite(H5T_NATIVE_LONG, long_buf); // no-warning + +#ifdef __CHAR_UNSIGNED__ + H5Dwrite(H5T_NATIVE_CHAR, int_buf); // expected-warning {{argument type 'int *' doesn't match specified 'hdf5' type tag that requires 'unsigned char *'}} +#else + H5Dwrite(H5T_NATIVE_CHAR, int_buf); // expected-warning {{argument type 'int *' doesn't match specified 'hdf5' type tag that requires 'signed char *'}} +#endif + H5Dwrite(H5T_NATIVE_INT, long_buf); // expected-warning {{argument type 'long *' doesn't match specified 'hdf5' type tag that requires 'int *'}} + + // FIXME: we should warn here, but it will cause false positives because + // different kinds may use same magic values. + //H5Dwrite(MPI_INT, int_buf); +} + diff --git a/test/Sema/warn-type-safety.c b/test/Sema/warn-type-safety.c new file mode 100644 index 000000000000..6f548aa2567d --- /dev/null +++ b/test/Sema/warn-type-safety.c @@ -0,0 +1,152 @@ +// RUN: %clang_cc1 -std=c99 -fsyntax-only -verify %s +// RUN: %clang_cc1 -x c++ -std=c++98 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c99 -fno-signed-char -fsyntax-only -verify %s + +struct A {}; + +typedef struct A *MPI_Datatype; + +int wrong1(void *buf, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag )); // expected-error {{attribute requires parameter 1 to be an identifier}} + +int wrong2(void *buf, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,0,7) )); // expected-error {{attribute parameter 2 is out of bounds}} + +int wrong3(void *buf, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,3,7) )); // expected-error {{attribute parameter 2 is out of bounds}} + +int wrong4(void *buf, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,1,0) )); // expected-error {{attribute parameter 3 is out of bounds}} + +int wrong5(void *buf, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,1,3) )); // expected-error {{attribute parameter 3 is out of bounds}} + +int wrong6(void *buf, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,0x8000000000000001ULL,1) )); // expected-error {{attribute parameter 2 is out of bounds}} + +extern int x; + +int wrong7(void *buf, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,x,2) )); // expected-error {{attribute requires parameter 2 to be an integer constant}} + +int wrong8(void *buf, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,1,x) )); // expected-error {{attribute requires parameter 3 to be an integer constant}} + +int wrong9 __attribute__(( pointer_with_type_tag(mpi,1,2) )); // expected-error {{attribute only applies to functions and methods}} + +int wrong10(double buf, MPI_Datatype type) + __attribute__(( pointer_with_type_tag(mpi,1,2) )); // expected-error {{'pointer_with_type_tag' attribute only applies to pointer arguments}} + + +extern struct A datatype_wrong1 + __attribute__(( type_tag_for_datatype )); // expected-error {{attribute requires parameter 1 to be an identifier}} + +extern struct A datatype_wrong2 + __attribute__(( type_tag_for_datatype(mpi,1,2) )); // expected-error {{expected a type}} + +extern struct A datatype_wrong3 + __attribute__(( type_tag_for_datatype(mpi,not_a_type) )); // expected-error {{unknown type name 'not_a_type'}} + +extern struct A datatype_wrong4 + __attribute__(( type_tag_for_datatype(mpi,int,int) )); // expected-error {{expected identifier}} + +extern struct A datatype_wrong5 + __attribute__(( type_tag_for_datatype(mpi,int,not_a_flag) )); // expected-error {{invalid comparison flag 'not_a_flag'}} + +extern struct A datatype_wrong6 + __attribute__(( type_tag_for_datatype(mpi,int,layout_compatible,not_a_flag) )); // expected-error {{invalid comparison flag 'not_a_flag'}} + + +// Using a tag with kind A in a place where the function requires kind B should +// warn. + +void A_func(void *ptr, void *tag) __attribute__(( pointer_with_type_tag(a,1,2) )); + +extern struct A A_tag __attribute__(( type_tag_for_datatype(a,int) )); +extern struct A B_tag __attribute__(( type_tag_for_datatype(b,int) )); + +void C_func(void *ptr, int tag) __attribute__(( pointer_with_type_tag(c,1,2) )); + +static const int C_tag __attribute__(( type_tag_for_datatype(c,int) )) = 10; +static const int D_tag __attribute__(( type_tag_for_datatype(d,int) )) = 20; + +void test_tag_mismatch(int *ptr) +{ + A_func(ptr, &A_tag); // no-warning + A_func(ptr, &B_tag); // expected-warning {{this type tag was not designed to be used with this function}} + C_func(ptr, C_tag); // no-warning + C_func(ptr, D_tag); // expected-warning {{this type tag was not designed to be used with this function}} + C_func(ptr, 10); // no-warning + C_func(ptr, 20); // should warn, but may cause false positives +} + +// Check that we look through typedefs in the special case of allowing 'char' +// to be matched with 'signed char' or 'unsigned char'. +void E_func(void *ptr, int tag) __attribute__(( pointer_with_type_tag(e,1,2) )); + +typedef char E_char; +typedef char E_char_2; +typedef signed char E_char_signed; +typedef unsigned char E_char_unsigned; + +static const int E_tag __attribute__(( type_tag_for_datatype(e,E_char) )) = 10; + +void test_char_typedef(char *char_buf, + E_char_2 *e_char_buf, + E_char_signed *e_char_signed_buf, + E_char_unsigned *e_char_unsigned_buf) +{ + E_func(char_buf, E_tag); + E_func(e_char_buf, E_tag); +#ifdef __CHAR_UNSIGNED__ + E_func(e_char_signed_buf, E_tag); // expected-warning {{argument type 'E_char_signed *' (aka 'signed char *') doesn't match specified 'e' type tag that requires 'E_char *' (aka 'char *')}} + E_func(e_char_unsigned_buf, E_tag); +#else + E_func(e_char_signed_buf, E_tag); + E_func(e_char_unsigned_buf, E_tag); // expected-warning {{argument type 'E_char_unsigned *' (aka 'unsigned char *') doesn't match specified 'e' type tag that requires 'E_char *' (aka 'char *')}} +#endif +} + +// Tests for argument_with_type_tag. + +#define F_DUPFD 10 +#define F_SETLK 20 + +struct flock { }; + +static const int F_DUPFD_tag __attribute__(( type_tag_for_datatype(fcntl,int) )) = F_DUPFD; +static const int F_SETLK_tag __attribute__(( type_tag_for_datatype(fcntl,struct flock *) )) = F_SETLK; + +int fcntl(int fd, int cmd, ...) __attribute__(( argument_with_type_tag(fcntl,3,2) )); + +void test_argument_with_type_tag(struct flock *f) +{ + fcntl(0, F_DUPFD, 10); // no-warning + fcntl(0, F_SETLK, f); // no-warning + + fcntl(0, F_SETLK, 10); // expected-warning {{argument type 'int' doesn't match specified 'fcntl' type tag that requires 'struct flock *'}} + fcntl(0, F_DUPFD, f); // expected-warning {{argument type 'struct flock *' doesn't match specified 'fcntl' type tag that requires 'int'}} +} + +void test_tag_expresssion(int b) { + fcntl(0, b ? F_DUPFD : F_SETLK, 10); // no-warning + fcntl(0, b + F_DUPFD, 10); // no-warning + fcntl(0, (b, F_DUPFD), 10); // expected-warning {{expression result unused}} +} + +// Check that using 64-bit magic values as tags works and tag values do not +// overflow internally. +void F_func(void *ptr, unsigned long long tag) __attribute__((pointer_with_type_tag(f,1,2) )); + +static const unsigned long long F_tag1 __attribute__(( type_tag_for_datatype(f,int) )) = 0xFFFFFFFFFFFFFFFFULL; +static const unsigned long long F_tag2 __attribute__(( type_tag_for_datatype(f,float) )) = 0xFFFFFFFFULL; + +void test_64bit_magic(int *int_ptr, float *float_ptr) +{ + F_func(int_ptr, 0xFFFFFFFFFFFFFFFFULL); + F_func(int_ptr, 0xFFFFFFFFULL); // expected-warning {{argument type 'int *' doesn't match specified 'f' type tag that requires 'float *'}} + F_func(float_ptr, 0xFFFFFFFFFFFFFFFFULL); // expected-warning {{argument type 'float *' doesn't match specified 'f' type tag that requires 'int *'}} + F_func(float_ptr, 0xFFFFFFFFULL); +} + + diff --git a/test/Sema/warn-type-safety.cpp b/test/Sema/warn-type-safety.cpp new file mode 100644 index 000000000000..d053fbaa21fc --- /dev/null +++ b/test/Sema/warn-type-safety.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +typedef struct ompi_datatype_t *MPI_Datatype; + +#define OMPI_PREDEFINED_GLOBAL(type, global) ((type) &(global)) + +#define MPI_FLOAT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_float) +#define MPI_INT OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_int) +#define MPI_NULL OMPI_PREDEFINED_GLOBAL(MPI_Datatype, ompi_mpi_null) + +extern struct ompi_predefined_datatype_t ompi_mpi_float __attribute__(( type_tag_for_datatype(mpi,float) )); +extern struct ompi_predefined_datatype_t ompi_mpi_int __attribute__(( type_tag_for_datatype(mpi,int) )); +extern struct ompi_predefined_datatype_t ompi_mpi_null __attribute__(( type_tag_for_datatype(mpi,void,must_be_null) )); + +int f(int x) { return x; } +static const int wrong_init __attribute__(( type_tag_for_datatype(zzz,int) )) = f(100); // expected-error {{'type_tag_for_datatype' attribute requires the initializer to be an integral constant expression}} + +//===--- Tests ------------------------------------------------------------===// +// Check that hidden 'this' is handled correctly. + +class C +{ +public: + void f1(void *buf, int count, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,5,6) )); // expected-error {{attribute parameter 2 is out of bounds}} + + void f2(void *buf, int count, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,2,5) )); // expected-error {{attribute parameter 3 is out of bounds}} + + void f3(void *buf, int count, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,1,5) )); // expected-error {{attribute is invalid for the implicit this argument}} + + void f4(void *buf, int count, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,2,1) )); // expected-error {{attribute is invalid for the implicit this argument}} + + void MPI_Send(void *buf, int count, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,2,4) )); // no-error +}; + +// Check that we don't crash on type and value dependent expressions. +template<int a> +void value_dep(void *buf, int count, MPI_Datatype datatype) + __attribute__(( pointer_with_type_tag(mpi,a,5) )); // expected-error {{attribute requires parameter 2 to be an integer constant}} + +class OperatorIntStar +{ +public: + operator int*(); +}; + +void test1(C *c, int *int_buf) +{ + c->MPI_Send(int_buf, 1, MPI_INT); // no-warning + c->MPI_Send(int_buf, 1, MPI_FLOAT); // expected-warning {{argument type 'int *' doesn't match specified 'mpi' type tag that requires 'float *'}} + + OperatorIntStar i; + c->MPI_Send(i, 1, MPI_INT); // no-warning + c->MPI_Send(i, 1, MPI_FLOAT); // expected-warning {{argument type 'int *' doesn't match specified 'mpi' type tag that requires 'float *'}} +} + +template<typename T> +void test2(C *c, int *int_buf, T tag) +{ + c->MPI_Send(int_buf, 1, tag); // no-warning +} + +void test3(C *c, int *int_buf) { + test2(c, int_buf, MPI_INT); + test2(c, int_buf, MPI_NULL); +} + diff --git a/test/SemaCXX/convert-to-bool.cpp b/test/SemaCXX/convert-to-bool.cpp index c9a355549fdf..b52f11c93d39 100644 --- a/test/SemaCXX/convert-to-bool.cpp +++ b/test/SemaCXX/convert-to-bool.cpp @@ -62,6 +62,5 @@ struct C { void test_copy_init_conversions(C c) { A &a = c; // expected-error{{no viable conversion from 'C' to 'A'}} - B &b = b; // okay + B &b = c; // okay } - diff --git a/test/SemaCXX/pragma-pack.cpp b/test/SemaCXX/pragma-pack.cpp index 1bc738b087a9..5c1d5c6c82af 100644 --- a/test/SemaCXX/pragma-pack.cpp +++ b/test/SemaCXX/pragma-pack.cpp @@ -32,3 +32,26 @@ struct Sub : virtual Base { int check[sizeof(Sub) == 13 ? 1 : -1]; } + +namespace llvm_support_endian { + +template<typename, bool> struct X; + +#pragma pack(push) +#pragma pack(1) +template<typename T> struct X<T, true> { + T t; +}; +#pragma pack(pop) + +#pragma pack(push) +#pragma pack(2) +template<> struct X<long double, true> { + long double c; +}; +#pragma pack(pop) + +int check1[__alignof(X<int, true>) == 1 ? 1 : -1]; +int check2[__alignof(X<long double, true>) == 2 ? 1 : -1]; + +} diff --git a/test/SemaCXX/references.cpp b/test/SemaCXX/references.cpp index 70d3799a0ea5..028c6909210e 100644 --- a/test/SemaCXX/references.cpp +++ b/test/SemaCXX/references.cpp @@ -136,4 +136,4 @@ namespace PR8608 { } // The following crashed trying to recursively evaluate the LValue. -const int &do_not_crash = do_not_crash; +const int &do_not_crash = do_not_crash; // expected-warning{{variable 'do_not_crash' is uninitialized when used within its own initialization}} diff --git a/test/SemaCXX/uninitialized.cpp b/test/SemaCXX/uninitialized.cpp index 13d287bf1af6..385548b51cca 100644 --- a/test/SemaCXX/uninitialized.cpp +++ b/test/SemaCXX/uninitialized.cpp @@ -316,3 +316,84 @@ namespace { G(char (*)[8]) : f3(new F(f3->*ptr)) {} // expected-warning {{field is uninitialized when used here}} }; } + +namespace statics { + static int a = a; // no-warning: used to signal intended lack of initialization. + static int b = b + 1; // expected-warning {{variable 'b' is uninitialized when used within its own initialization}} + static int c = (c + c); // expected-warning 2{{variable 'c' is uninitialized when used within its own initialization}} + static int e = static_cast<long>(e) + 1; // expected-warning {{variable 'e' is uninitialized when used within its own initialization}} + static int f = foo(f); // expected-warning {{variable 'f' is uninitialized when used within its own initialization}} + + // Thes don't warn as they don't require the value. + static int g = sizeof(g); + int gg = g; // Silence unneeded warning + static void* ptr = &ptr; + static int h = bar(&h); + static int i = boo(i); + static int j = far(j); + static int k = __alignof__(k); + + static int l = k ? l : l; // expected-warning 2{{variable 'l' is uninitialized when used within its own initialization}} + static int m = 1 + (k ? m : m); // expected-warning 2{{variable 'm' is uninitialized when used within its own initialization}} + static int n = -n; // expected-warning {{variable 'n' is uninitialized when used within its own initialization}} + + void test() { + static int a = a; // no-warning: used to signal intended lack of initialization. + static int b = b + 1; // expected-warning {{variable 'b' is uninitialized when used within its own initialization}} + static int c = (c + c); // expected-warning 2{{variable 'c' is uninitialized when used within its own initialization}} + static int d = ({ d + d ;}); // expected-warning 2{{variable 'd' is uninitialized when used within its own initialization}} + static int e = static_cast<long>(e) + 1; // expected-warning {{variable 'e' is uninitialized when used within its own initialization}} + static int f = foo(f); // expected-warning {{variable 'f' is uninitialized when used within its own initialization}} + + // Thes don't warn as they don't require the value. + static int g = sizeof(g); + static void* ptr = &ptr; + static int h = bar(&h); + static int i = boo(i); + static int j = far(j); + static int k = __alignof__(k); + + static int l = k ? l : l; // expected-warning 2{{variable 'l' is uninitialized when used within its own initialization}} + static int m = 1 + (k ? m : m); // expected-warning 2{{variable 'm' is uninitialized when used within its own initialization}} + static int n = -n; // expected-warning {{variable 'n' is uninitialized when used within its own initialization}} + for (;;) { + static int a = a; // no-warning: used to signal intended lack of initialization. + static int b = b + 1; // expected-warning {{variable 'b' is uninitialized when used within its own initialization}} + static int c = (c + c); // expected-warning 2{{variable 'c' is uninitialized when used within its own initialization}} + static int d = ({ d + d ;}); // expected-warning 2{{variable 'd' is uninitialized when used within its own initialization}} + static int e = static_cast<long>(e) + 1; // expected-warning {{variable 'e' is uninitialized when used within its own initialization}} + static int f = foo(f); // expected-warning {{variable 'f' is uninitialized when used within its own initialization}} + + // Thes don't warn as they don't require the value. + static int g = sizeof(g); + static void* ptr = &ptr; + static int h = bar(&h); + static int i = boo(i); + static int j = far(j); + static int k = __alignof__(k); + + static int l = k ? l : l; // expected-warning 2{{variable 'l' is uninitialized when used within its own initialization}} + static int m = 1 + (k ? m : m); // expected-warning 2{{variable 'm' is uninitialized when used within its own initialization}} + static int n = -n; // expected-warning {{variable 'n' is uninitialized when used within its own initialization}} + } + } +} + +namespace references { + int &a = a; // expected-warning{{variable 'a' is uninitialized when used within its own initialization}} + + struct S { + S() : a(a) {} // expected-warning{{field is uninitialized when used here}} + int &a; + }; + + void f() { + int &a = a; // expected-warning{{variable 'a' is uninitialized when used within its own initialization}} + } + + struct T { + T() : a(b), b(a) {} // FIXME: Warn here. + int &a, &b; + int &c = c; // FIXME: Warn here. + }; +} diff --git a/test/SemaCXX/warn-thread-safety-parsing.cpp b/test/SemaCXX/warn-thread-safety-parsing.cpp index 3f8a7359088d..8aa6a91a9d2d 100644 --- a/test/SemaCXX/warn-thread-safety-parsing.cpp +++ b/test/SemaCXX/warn-thread-safety-parsing.cpp @@ -1429,4 +1429,14 @@ class Foo { } +namespace InvalidDeclTest { + +class Foo { }; +namespace { +void Foo::bar(Mutex* mu) LOCKS_EXCLUDED(mu) { } // \ + // expected-error {{cannot define or redeclare 'bar' here because namespace '' does not enclose namespace 'Foo'}} \ + // expected-warning {{attribute locks_excluded ignored, because it is not attached to a declaration}} +} + +} // end namespace InvalidDeclTest diff --git a/test/SemaObjC/warn-cast-of-sel-expr.m b/test/SemaObjC/warn-cast-of-sel-expr.m new file mode 100644 index 000000000000..97915a0094ef --- /dev/null +++ b/test/SemaObjC/warn-cast-of-sel-expr.m @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-value %s +// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -verify -Wcast-of-sel-type -Wno-unused-value %s +// rdar://12107381 + +SEL s; + +SEL sel_registerName(const char *); + +int main() { +(char *)s; // expected-warning {{cast of type 'SEL' to 'char *' is deprecated; use sel_getName instead}} +(void *)s; // ok +(const char *)sel_registerName("foo"); // expected-warning {{cast of type 'SEL' to 'const char *' is deprecated; use sel_getName instead}} + +(const void *)sel_registerName("foo"); // ok + +(void) s; // ok + +(void *const)s; // ok + +(const void *const)s; // ok +} diff --git a/test/SemaObjCXX/abstract-class-type-ivar.mm b/test/SemaObjCXX/abstract-class-type-ivar.mm new file mode 100644 index 000000000000..823e9c197d35 --- /dev/null +++ b/test/SemaObjCXX/abstract-class-type-ivar.mm @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-objc-root-class %s +// rdar://12095239 + +class CppAbstractBase { +public: + virtual void testA() = 0; + virtual void testB() = 0; // expected-note {{unimplemented pure virtual method 'testB' in 'CppConcreteSub}} + int a; +}; + +class CppConcreteSub : public CppAbstractBase { + virtual void testA() { } +}; + +@interface Objc { + CppConcreteSub _concrete; // expected-error{{ivar type 'CppConcreteSub' is an abstract class}} +} +- (CppAbstractBase*)abstract; +@end +@implementation Objc +- (CppAbstractBase*)abstract { + return &_concrete; +} +@end + +class Cpp { +public: + CppConcreteSub sub; // expected-error {{field type 'CppConcreteSub' is an abstract class}} +}; diff --git a/test/Tooling/clang-check-ast-dump.cpp b/test/Tooling/clang-check-ast-dump.cpp index 86533af3e11d..e29072bcfe87 100644 --- a/test/Tooling/clang-check-ast-dump.cpp +++ b/test/Tooling/clang-check-ast-dump.cpp @@ -22,6 +22,10 @@ // CHECK-LIST-NEXT: test_namespace::TheClass // CHECK-LIST-NEXT: test_namespace::TheClass::theMethod // CHECK-LIST-NEXT: x +// +// RUN: clang-check -ast-dump -ast-dump-filter test_namespace::TheClass::n "%s" -- 2>&1 | FileCheck -check-prefix CHECK-ATTR %s +// CHECK-ATTR: test_namespace +// CHECK-ATTR-NEXT: int n __attribute__((aligned((BinaryOperator namespace test_namespace { @@ -30,6 +34,7 @@ public: int theMethod(int x) { return x + x; } + int n __attribute__((aligned(1+1))); }; } diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index cf37c7d27a9c..adf0e9479f08 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -1099,6 +1099,12 @@ TEST(Returns, MatchesReturnTypes) { function(returns(hasDeclaration(record(hasName("Y"))))))); } +TEST(IsExternC, MatchesExternCFunctionDeclarations) { + EXPECT_TRUE(matches("extern \"C\" void f() {}", function(isExternC()))); + EXPECT_TRUE(matches("extern \"C\" { void f() {} }", function(isExternC()))); + EXPECT_TRUE(notMatches("void f() {}", function(isExternC()))); +} + TEST(HasAnyParameter, DoesntMatchIfInnerMatcherDoesntMatch) { EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };", method(hasAnyParameter(hasType(record(hasName("X"))))))); @@ -2023,6 +2029,28 @@ TEST(IsConstQualified, DoesNotMatchInappropriately) { variable(hasType(isConstQualified())))); } +TEST(CastExpression, MatchesExplicitCasts) { + EXPECT_TRUE(matches("char *p = reinterpret_cast<char *>(&p);", + expression(castExpr()))); + EXPECT_TRUE(matches("void *p = (void *)(&p);", expression(castExpr()))); + EXPECT_TRUE(matches("char q, *p = const_cast<char *>(&q);", + expression(castExpr()))); + EXPECT_TRUE(matches("char c = char(0);", expression(castExpr()))); +} +TEST(CastExpression, MatchesImplicitCasts) { + // This test creates an implicit cast from int to char. + EXPECT_TRUE(matches("char c = 0;", expression(castExpr()))); + // This test creates an implicit cast from lvalue to rvalue. + EXPECT_TRUE(matches("char c = 0, d = c;", expression(castExpr()))); +} + +TEST(CastExpression, DoesNotMatchNonCasts) { + EXPECT_TRUE(notMatches("char c = '0';", expression(castExpr()))); + EXPECT_TRUE(notMatches("char c, &q = c;", expression(castExpr()))); + EXPECT_TRUE(notMatches("int i = (0);", expression(castExpr()))); + EXPECT_TRUE(notMatches("int i = 0;", expression(castExpr()))); +} + TEST(ReinterpretCast, MatchesSimpleCase) { EXPECT_TRUE(matches("char* p = reinterpret_cast<char*>(&p);", expression(reinterpretCast()))); @@ -2089,6 +2117,201 @@ TEST(HasDestinationType, MatchesSimpleCase) { pointsTo(TypeMatcher(anything()))))))); } +TEST(HasImplicitDestinationType, MatchesSimpleCase) { + // This test creates an implicit const cast. + EXPECT_TRUE(matches("int x; const int i = x;", + expression(implicitCast( + hasImplicitDestinationType(isInteger()))))); + // This test creates an implicit array-to-pointer cast. + EXPECT_TRUE(matches("int arr[3]; int *p = arr;", + expression(implicitCast(hasImplicitDestinationType( + pointsTo(TypeMatcher(anything()))))))); +} + +TEST(HasImplicitDestinationType, DoesNotMatchIncorrectly) { + // This test creates an implicit cast from int to char. + EXPECT_TRUE(notMatches("char c = 0;", + expression(implicitCast(hasImplicitDestinationType( + unless(anything())))))); + // This test creates an implicit array-to-pointer cast. + EXPECT_TRUE(notMatches("int arr[3]; int *p = arr;", + expression(implicitCast(hasImplicitDestinationType( + unless(anything())))))); +} + +TEST(ImplicitCast, MatchesSimpleCase) { + // This test creates an implicit const cast. + EXPECT_TRUE(matches("int x = 0; const int y = x;", + variable(hasInitializer(implicitCast())))); + // This test creates an implicit cast from int to char. + EXPECT_TRUE(matches("char c = 0;", + variable(hasInitializer(implicitCast())))); + // This test creates an implicit array-to-pointer cast. + EXPECT_TRUE(matches("int arr[6]; int *p = arr;", + variable(hasInitializer(implicitCast())))); +} + +TEST(ImplicitCast, DoesNotMatchIncorrectly) { + // This test verifies that implicitCast() matches exactly when implicit casts + // are present, and that it ignores explicit and paren casts. + + // These two test cases have no casts. + EXPECT_TRUE(notMatches("int x = 0;", + variable(hasInitializer(implicitCast())))); + EXPECT_TRUE(notMatches("int x = 0, &y = x;", + variable(hasInitializer(implicitCast())))); + + EXPECT_TRUE(notMatches("int x = 0; double d = (double) x;", + variable(hasInitializer(implicitCast())))); + EXPECT_TRUE(notMatches("const int *p; int *q = const_cast<int *>(p);", + variable(hasInitializer(implicitCast())))); + + EXPECT_TRUE(notMatches("int x = (0);", + variable(hasInitializer(implicitCast())))); +} + +TEST(IgnoringImpCasts, MatchesImpCasts) { + // This test checks that ignoringImpCasts matches when implicit casts are + // present and its inner matcher alone does not match. + // Note that this test creates an implicit const cast. + EXPECT_TRUE(matches("int x = 0; const int y = x;", + variable(hasInitializer(ignoringImpCasts( + declarationReference(to(variable(hasName("x"))))))))); + // This test creates an implict cast from int to char. + EXPECT_TRUE(matches("char x = 0;", + variable(hasInitializer(ignoringImpCasts( + integerLiteral(equals(0))))))); +} + +TEST(IgnoringImpCasts, DoesNotMatchIncorrectly) { + // These tests verify that ignoringImpCasts does not match if the inner + // matcher does not match. + // Note that the first test creates an implicit const cast. + EXPECT_TRUE(notMatches("int x; const int y = x;", + variable(hasInitializer(ignoringImpCasts( + unless(anything())))))); + EXPECT_TRUE(notMatches("int x; int y = x;", + variable(hasInitializer(ignoringImpCasts( + unless(anything())))))); + + // These tests verify that ignoringImplictCasts does not look through explicit + // casts or parentheses. + EXPECT_TRUE(notMatches("char* p = static_cast<char*>(0);", + variable(hasInitializer(ignoringImpCasts( + integerLiteral()))))); + EXPECT_TRUE(notMatches("int i = (0);", + variable(hasInitializer(ignoringImpCasts( + integerLiteral()))))); + EXPECT_TRUE(notMatches("float i = (float)0;", + variable(hasInitializer(ignoringImpCasts( + integerLiteral()))))); + EXPECT_TRUE(notMatches("float i = float(0);", + variable(hasInitializer(ignoringImpCasts( + integerLiteral()))))); +} + +TEST(IgnoringImpCasts, MatchesWithoutImpCasts) { + // This test verifies that expressions that do not have implicit casts + // still match the inner matcher. + EXPECT_TRUE(matches("int x = 0; int &y = x;", + variable(hasInitializer(ignoringImpCasts( + declarationReference(to(variable(hasName("x"))))))))); +} + +TEST(IgnoringParenCasts, MatchesParenCasts) { + // This test checks that ignoringParenCasts matches when parentheses and/or + // casts are present and its inner matcher alone does not match. + EXPECT_TRUE(matches("int x = (0);", + variable(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); + EXPECT_TRUE(matches("int x = (((((0)))));", + variable(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); + + // This test creates an implict cast from int to char in addition to the + // parentheses. + EXPECT_TRUE(matches("char x = (0);", + variable(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); + + EXPECT_TRUE(matches("char x = (char)0;", + variable(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); + EXPECT_TRUE(matches("char* p = static_cast<char*>(0);", + variable(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); +} + +TEST(IgnoringParenCasts, MatchesWithoutParenCasts) { + // This test verifies that expressions that do not have any casts still match. + EXPECT_TRUE(matches("int x = 0;", + variable(hasInitializer(ignoringParenCasts( + integerLiteral(equals(0))))))); +} + +TEST(IgnoringParenCasts, DoesNotMatchIncorrectly) { + // These tests verify that ignoringImpCasts does not match if the inner + // matcher does not match. + EXPECT_TRUE(notMatches("int x = ((0));", + variable(hasInitializer(ignoringParenCasts( + unless(anything())))))); + + // This test creates an implicit cast from int to char in addition to the + // parentheses. + EXPECT_TRUE(notMatches("char x = ((0));", + variable(hasInitializer(ignoringParenCasts( + unless(anything())))))); + + EXPECT_TRUE(notMatches("char *x = static_cast<char *>((0));", + variable(hasInitializer(ignoringParenCasts( + unless(anything())))))); +} + +TEST(IgnoringParenAndImpCasts, MatchesParenImpCasts) { + // This test checks that ignoringParenAndImpCasts matches when + // parentheses and/or implicit casts are present and its inner matcher alone + // does not match. + // Note that this test creates an implicit const cast. + EXPECT_TRUE(matches("int x = 0; const int y = x;", + variable(hasInitializer(ignoringParenImpCasts( + declarationReference(to(variable(hasName("x"))))))))); + // This test creates an implicit cast from int to char. + EXPECT_TRUE(matches("const char x = (0);", + variable(hasInitializer(ignoringParenImpCasts( + integerLiteral(equals(0))))))); +} + +TEST(IgnoringParenAndImpCasts, MatchesWithoutParenImpCasts) { + // This test verifies that expressions that do not have parentheses or + // implicit casts still match. + EXPECT_TRUE(matches("int x = 0; int &y = x;", + variable(hasInitializer(ignoringParenImpCasts( + declarationReference(to(variable(hasName("x"))))))))); + EXPECT_TRUE(matches("int x = 0;", + variable(hasInitializer(ignoringParenImpCasts( + integerLiteral(equals(0))))))); +} + +TEST(IgnoringParenAndImpCasts, DoesNotMatchIncorrectly) { + // These tests verify that ignoringParenImpCasts does not match if + // the inner matcher does not match. + // This test creates an implicit cast. + EXPECT_TRUE(notMatches("char c = ((3));", + variable(hasInitializer(ignoringParenImpCasts( + unless(anything())))))); + // These tests verify that ignoringParenAndImplictCasts does not look + // through explicit casts. + EXPECT_TRUE(notMatches("float y = (float(0));", + variable(hasInitializer(ignoringParenImpCasts( + integerLiteral()))))); + EXPECT_TRUE(notMatches("float y = (float)0;", + variable(hasInitializer(ignoringParenImpCasts( + integerLiteral()))))); + EXPECT_TRUE(notMatches("char* p = static_cast<char*>(0);", + variable(hasInitializer(ignoringParenImpCasts( + integerLiteral()))))); +} + TEST(HasSourceExpression, MatchesImplicitCasts) { EXPECT_TRUE(matches("class string {}; class URL { public: URL(string s); };" "void r() {string a_string; URL url = a_string; }", @@ -2154,6 +2377,40 @@ TEST(UsingDeclaration, ThroughUsingDeclaration) { declarationReference(throughUsingDecl(anything())))); } +TEST(SingleDecl, IsSingleDecl) { + StatementMatcher SingleDeclStmt = + declarationStatement(hasSingleDecl(variable(hasInitializer(anything())))); + EXPECT_TRUE(matches("void f() {int a = 4;}", SingleDeclStmt)); + EXPECT_TRUE(notMatches("void f() {int a;}", SingleDeclStmt)); + EXPECT_TRUE(notMatches("void f() {int a = 4, b = 3;}", + SingleDeclStmt)); +} + +TEST(DeclStmt, ContainsDeclaration) { + DeclarationMatcher MatchesInit = variable(hasInitializer(anything())); + + EXPECT_TRUE(matches("void f() {int a = 4;}", + declarationStatement(containsDeclaration(0, + MatchesInit)))); + EXPECT_TRUE(matches("void f() {int a = 4, b = 3;}", + declarationStatement(containsDeclaration(0, MatchesInit), + containsDeclaration(1, + MatchesInit)))); + unsigned WrongIndex = 42; + EXPECT_TRUE(notMatches("void f() {int a = 4, b = 3;}", + declarationStatement(containsDeclaration(WrongIndex, + MatchesInit)))); +} + +TEST(DeclCount, DeclCountIsCorrect) { + EXPECT_TRUE(matches("void f() {int i,j;}", + declarationStatement(declCountIs(2)))); + EXPECT_TRUE(notMatches("void f() {int i,j; int k;}", + declarationStatement(declCountIs(3)))); + EXPECT_TRUE(notMatches("void f() {int i,j, k, l;}", + declarationStatement(declCountIs(3)))); +} + TEST(While, MatchesWhileLoops) { EXPECT_TRUE(notMatches("void x() {}", whileStmt())); EXPECT_TRUE(matches("void x() { while(true); }", whileStmt())); diff --git a/unittests/Tooling/RecursiveASTVisitorTest.cpp b/unittests/Tooling/RecursiveASTVisitorTest.cpp index f3ba6468d038..4b539067b136 100644 --- a/unittests/Tooling/RecursiveASTVisitorTest.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTest.cpp @@ -385,4 +385,11 @@ TEST(RecursiveASTVisitor, VisitsImplicitCopyConstructors) { "int main() { Simple s; Simple t(s); }\n")); } +TEST(RecursiveASTVisitor, VisitsExtension) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("s", 1, 24); + EXPECT_TRUE(Visitor.runOver( + "int s = __extension__ (s);\n")); +} + } // end namespace clang diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 1b1a478ceb13..ef1ad3e1d2d9 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -349,7 +349,9 @@ namespace { << "Type(), Record);\n"; } void writeValue(raw_ostream &OS) const { - OS << "\" << get" << getUpperName() << "(Ctx) << \""; + OS << "\";\n" + << " " << getLowerName() << "Expr->printPretty(OS, 0, Policy);\n" + << " OS << \""; } }; @@ -728,7 +730,8 @@ void EmitClangAttrClass(RecordKeeper &Records, raw_ostream &OS) { OS << " }\n\n"; OS << " virtual " << R.getName() << "Attr *clone (ASTContext &C) const;\n"; - OS << " virtual void printPretty(llvm::raw_ostream &OS, ASTContext &Ctx) const;\n"; + OS << " virtual void printPretty(llvm::raw_ostream &OS," + << " const PrintingPolicy &Policy) const;\n"; for (ai = Args.begin(); ai != ae; ++ai) { (*ai)->writeAccessors(OS); @@ -786,7 +789,7 @@ void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { OS << ");\n}\n\n"; OS << "void " << R.getName() << "Attr::printPretty(" - << "llvm::raw_ostream &OS, ASTContext &Ctx) const {\n"; + << "llvm::raw_ostream &OS, const PrintingPolicy &Policy) const {\n"; if (Spellings.begin() != Spellings.end()) { std::string Spelling = (*Spellings.begin())->getValueAsString("Name"); OS << " OS << \" __attribute__((" << Spelling; diff --git a/utils/analyzer/CmpRuns.py b/utils/analyzer/CmpRuns.py index f2961cf0acf5..c8f05cbcf474 100755 --- a/utils/analyzer/CmpRuns.py +++ b/utils/analyzer/CmpRuns.py @@ -146,7 +146,9 @@ def loadResults(path, opts, root = "", deleteEmpty=True): for d in data['diagnostics']: # FIXME: Why is this named files, when does it have multiple # files? - assert len(d['HTMLDiagnostics_files']) == 1 + # TODO: Add the assert back in after we fix the + # plist-html output. + # assert len(d['HTMLDiagnostics_files']) == 1 htmlFiles.append(d.pop('HTMLDiagnostics_files')[0]) else: htmlFiles = [None] * len(data['diagnostics']) diff --git a/www/comparison.html b/www/comparison.html index 58c4b31e2f66..01b8aea06905 100644 --- a/www/comparison.html +++ b/www/comparison.html @@ -103,9 +103,9 @@ sometimes acceptable, but are often confusing and it does not support expressive diagnostics. Clang also preserves typedefs in diagnostics consistently, showing macro expansions and many other features.</li> - <li>GCC is licensed under the GPL license. clang uses a BSD license, which - allows it to be used by projects that do not themselves want to be - GPL.</li> + <li>GCC is licensed under the GPL license. <a href="features.html#license"> + clang uses a BSD license,</a> which allows it to be embedded in + software that is not GPL-licensed.</li> <li>Clang inherits a number of features from its use of LLVM as a backend, including support for a bytecode representation for intermediate code, pluggable optimizers, link-time optimization support, Just-In-Time |