Vendor import of clang trunk r162107:
http://llvm.org/svn/llvm-project/cfe/trunk@162107
This commit is contained in:
parent
56d91b49b1
commit
657bc3d984
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/clang/dist/; revision=239392 svn path=/vendor/clang/clang-trunk-r162107/; revision=239393; tag=vendor/clang/clang-trunk-r162107
@ -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>
|
||||
|
@ -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>
|
||||
<!-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = -->
|
||||
|
96
docs/analyzer/IPA.txt
Normal file
96
docs/analyzer/IPA.txt
Normal file
@ -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.
|
@ -474,8 +474,17 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
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 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
/// 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 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
|
||||
/// \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.
|
||||
|
@ -105,7 +105,8 @@ class Attr {
|
||||
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; }
|
||||
|
@ -35,14 +35,14 @@ class CommandTraits {
|
||||
/// 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 @@ class CommandTraits {
|
||||
|
||||
private:
|
||||
struct VerbatimBlockCommand {
|
||||
StringRef BeginName;
|
||||
StringRef StartName;
|
||||
StringRef EndName;
|
||||
};
|
||||
|
||||
|
@ -858,10 +858,10 @@ class Decl {
|
||||
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:
|
||||
|
@ -1646,14 +1646,17 @@ class CXXMethodDecl : public FunctionDecl {
|
||||
/// \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.
|
||||
|
@ -26,7 +26,11 @@ class DeclGroupIterator;
|
||||
|
||||
class DeclGroup {
|
||||
// FIXME: Include a TypeSpecifier object.
|
||||
unsigned NumDecls;
|
||||
union {
|
||||
unsigned NumDecls;
|
||||
|
||||
Decl *Aligner;
|
||||
};
|
||||
|
||||
private:
|
||||
DeclGroup() : NumDecls(0) {}
|
||||
|
@ -67,7 +67,7 @@ class DeclContext::all_lookups_iterator {
|
||||
|
||||
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());
|
||||
|
@ -34,19 +34,19 @@ class PrinterHelper {
|
||||
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
|
||||
|
@ -55,16 +55,11 @@ class RawComment {
|
||||
|
||||
/// 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 @@ class RawComment {
|
||||
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 @@ class RawComment {
|
||||
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 @@ class RawComment {
|
||||
|
||||
const char *extractBriefText(const ASTContext &Context) const;
|
||||
|
||||
comments::FullComment *parse(const ASTContext &Context) const;
|
||||
|
||||
friend class ASTReader;
|
||||
};
|
||||
|
||||
|
@ -373,14 +373,8 @@ class Stmt {
|
||||
|
||||
/// 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;
|
||||
|
||||
@ -1620,36 +1614,40 @@ class AsmStmt : public Stmt {
|
||||
/// 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 @@ class MSAsmStmt : public Stmt {
|
||||
//===--- 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);
|
||||
|
@ -510,17 +510,23 @@ class TemplateArgumentListInfo {
|
||||
/// 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);
|
||||
|
@ -1061,7 +1061,6 @@ class RValueReferenceTypeLoc :
|
||||
struct FunctionLocInfo {
|
||||
SourceLocation LocalRangeBegin;
|
||||
SourceLocation LocalRangeEnd;
|
||||
bool TrailingReturn;
|
||||
};
|
||||
|
||||
/// \brief Wrapper for source info for functions.
|
||||
@ -1084,13 +1083,6 @@ class FunctionTypeLoc : public ConcreteTypeLoc<UnqualTypeLoc,
|
||||
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 @@ class FunctionTypeLoc : public ConcreteTypeLoc<UnqualTypeLoc,
|
||||
void initializeLocal(ASTContext &Context, SourceLocation Loc) {
|
||||
setLocalRangeBegin(Loc);
|
||||
setLocalRangeEnd(Loc);
|
||||
setTrailingReturn(false);
|
||||
for (unsigned i = 0, e = getNumArgs(); i != e; ++i)
|
||||
setArg(i, NULL);
|
||||
}
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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 {
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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<
|
||||
|
@ -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">;
|
||||
|
@ -101,7 +101,7 @@ class PTHManager : public IdentifierInfoLookup {
|
||||
|
||||
public:
|
||||
// The current PTH version.
|
||||
enum { Version = 9 };
|
||||
enum { Version = 10 };
|
||||
|
||||
~PTHManager();
|
||||
|
||||
|
@ -1834,6 +1834,10 @@ class Parser : public CodeCompletionHandler {
|
||||
ParsedAttributes &Attrs,
|
||||
SourceLocation *EndLoc);
|
||||
|
||||
void ParseTypeTagForDatatypeAttribute(IdentifierInfo &AttrName,
|
||||
SourceLocation AttrNameLoc,
|
||||
ParsedAttributes &Attrs,
|
||||
SourceLocation *EndLoc);
|
||||
|
||||
void ParseTypeofSpecifier(DeclSpec &DS);
|
||||
SourceLocation ParseDecltypeSpecifier(DeclSpec &DS);
|
||||
|
@ -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 @@ class AttributeList { // TODO: This should really be called ParsedAttribute
|
||||
/// 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 @@ class AttributeList { // TODO: This should really be called ParsedAttribute
|
||||
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 @@ class AttributeList { // TODO: This should really be called ParsedAttribute
|
||||
|
||||
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 @@ class AttributeList { // TODO: This should really be called ParsedAttribute
|
||||
: 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 @@ class AttributeList { // TODO: This should really be called ParsedAttribute
|
||||
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 @@ class AttributeList { // TODO: This should really be called ParsedAttribute
|
||||
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 @@ class AttributeList { // TODO: This should really be called ParsedAttribute
|
||||
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 @@ class AttributeFactory {
|
||||
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 @@ class AttributePool {
|
||||
|
||||
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 @@ class ParsedAttributes {
|
||||
/// 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 @@ class ParsedAttributes {
|
||||
return attr;
|
||||
}
|
||||
|
||||
/// Add availability attribute.
|
||||
AttributeList *addNew(IdentifierInfo *attrName, SourceRange attrRange,
|
||||
IdentifierInfo *scopeName, SourceLocation scopeLoc,
|
||||
IdentifierInfo *parmName, SourceLocation parmLoc,
|
||||
@ -533,6 +614,24 @@ class ParsedAttributes {
|
||||
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 =
|
||||
|
@ -2498,13 +2498,11 @@ class Sema {
|
||||
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 @@ class Sema {
|
||||
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 @@ class Sema {
|
||||
AbstractParamType,
|
||||
AbstractVariableType,
|
||||
AbstractFieldType,
|
||||
AbstractIvarType,
|
||||
AbstractArrayType
|
||||
};
|
||||
|
||||
@ -7146,6 +7145,42 @@ class Sema {
|
||||
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.
|
||||
|
@ -81,15 +81,19 @@ class BugReport : public llvm::ilist_node<BugReport> {
|
||||
typedef llvm::DenseSet<SymbolRef> Symbols;
|
||||
typedef llvm::DenseSet<const MemRegion *> Regions;
|
||||
|
||||
/// 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.
|
||||
Symbols interestingSymbols;
|
||||
/// 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 set of regions that are registered with this report as being
|
||||
/// 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.
|
||||
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 @@ class BugReport : public llvm::ilist_node<BugReport> {
|
||||
/// 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 @@ class BugReport : public llvm::ilist_node<BugReport> {
|
||||
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 @@ class BugReporter {
|
||||
/// 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 @@ class BugReporter {
|
||||
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 @@ class BugReporter {
|
||||
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 @@ class GRBugReporter : public BugReporter {
|
||||
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) {
|
||||
|
@ -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 @@ class PathDiagnosticPiece : public RefCountedBaseVPTR {
|
||||
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;
|
||||
|
@ -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
|
||||
|
@ -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 @@ class AnalysisManager : public BugReporterData {
|
||||
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 @@ class AnalysisManager : public BugReporterData {
|
||||
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 @@ class AnalysisManager : public BugReporterData {
|
||||
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; }
|
||||
|
@ -328,7 +328,7 @@ class CallEvent {
|
||||
|
||||
// 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 @@ class ObjCMethodCall : public CallEvent {
|
||||
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:
|
||||
|
@ -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) {}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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';
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -3083,9 +3083,8 @@ class ARMTargetInfo : public TargetInfo {
|
||||
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
|
||||
|
@ -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());
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 &&
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
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 {
|
||||
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;
|
||||
|
||||
// The asm keyword is a statement separator, so multiple asm statements
|
||||
// are allowed.
|
||||
if (!InAsmComment && Tok.is(tok::kw_asm))
|
||||
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 && InBraces && Tok.is(tok::r_brace) &&
|
||||
BraceCount == (savedBraceCount + 1)) {
|
||||
// Consume the closing brace, and finish
|
||||
EndLoc = ConsumeBrace();
|
||||
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 we hit EOF, we're done, period.
|
||||
if (Tok.is(tok::eof))
|
||||
break;
|
||||
EndLoc = ConsumeToken();
|
||||
|
||||
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 && InBraces && Tok.is(tok::r_brace) &&
|
||||
BraceCount == (savedBraceCount + 1)) {
|
||||
// Consume the closing brace, and finish
|
||||
EndLoc = ConsumeBrace();
|
||||
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);
|
||||
|
||||
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.
|
||||
|
@ -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]);
|
||||
|
@ -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]);
|
||||
|
@ -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*));
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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, ...");
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
// 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 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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
// Check the operands.
|
||||
for (unsigned j = (i == 0) ? 0 : LineEnds[i-1], e = LineEnds[i]; j != e; ++j) {
|
||||
// Determine if this should be considered a new asm.
|
||||
bool isNewAsm = i == 0 || AsmToks[i].isAtStartOfLine() ||
|
||||
AsmToks[i].is(tok::kw_asm);
|
||||
|
||||
IdentifierInfo *II;
|
||||
if (j == 0 || (i > 0 && j == LineEnds[i-1])) {
|
||||
II = AsmToks[j].getIdentifierInfo();
|
||||
Asm = II->getName().str();
|
||||
continue;
|
||||
// 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.");
|
||||
}
|
||||
|
||||
if (needSpaceAsmToken(AsmToks[j]))
|
||||
Asm += " ";
|
||||
|
||||
switch (AsmToks[j].getKind()) {
|
||||
default:
|
||||
//llvm_unreachable("Unknown token.");
|
||||
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!");
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO: Lookup the identifier.
|
||||
IsSimple = false;
|
||||
}
|
||||
} // AsmToks[i].getKind()
|
||||
}
|
||||
AsmStrings[i] = Asm.c_str();
|
||||
|
||||
// Start a new asm string with the opcode.
|
||||
if (isNewAsm) {
|
||||
AsmRegs[NumAsmStrings].resize(AsmToks.size());
|
||||
AsmNames[NumAsmStrings].resize(AsmToks.size());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
IsSimple = false;
|
||||
|
||||
// MS-style inline asm keywords require special handling.
|
||||
if (isMSAsmKeyword(Name)) {
|
||||
IsSimple = false;
|
||||
Asm += Name;
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: Why are we missing this segment register?
|
||||
if (Name == "fs") {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)) {
|
||||
|
@ -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) {
|
||||
|
@ -1185,10 +1185,10 @@ class TreeTransform {
|
||||
/// 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 @@ class TreeTransform {
|
||||
/// 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(),
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
@ -1635,62 +1607,6 @@ void RetainSummaryManager::InitializeMethodSummaries() {
|
||||
"info", NULL);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
@ -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
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// If the path is empty, generate a single step path with the location
|
||||
// of the issue.
|
||||
if (D->path.empty()) {
|
||||
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);
|
||||
}
|
||||
|
||||
// Get the meta data.
|
||||
const BugReport::ExtraTextList &Meta =
|
||||
exampleReport->getExtraText();
|
||||
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;
|
||||
}
|
||||
|
||||
// Emit a full diagnostic for the path if we have a PathDiagnosticConsumer.
|
||||
if (!PD)
|
||||
return;
|
||||
|
||||
if (D->path.empty()) {
|
||||
PathDiagnosticPiece *piece = new PathDiagnosticEventPiece(
|
||||
exampleReport->getLocation(getSourceManager()),
|
||||
exampleReport->getDescription());
|
||||
for ( ; Beg != End; ++Beg)
|
||||
piece->addRange(*Beg);
|
||||
|
||||
D->getActivePath().push_back(piece);
|
||||
}
|
||||
|
||||
PD->HandlePathDiagnostic(D.take());
|
||||
PD.HandlePathDiagnostic(D.take());
|
||||
}
|
||||
|
||||
void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
|
||||
|
@ -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 =
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -45,7 +45,7 @@ class HTMLDiagnostics : public PathDiagnosticConsumer {
|
||||
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 @@ class HTMLDiagnostics : public PathDiagnosticConsumer {
|
||||
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) {
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -32,7 +32,7 @@ class TextPathDiagnostics : public PathDiagnosticConsumer {
|
||||
: 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 @@ class TextPathDiagnostics : public PathDiagnosticConsumer {
|
||||
|
||||
} // 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;
|
||||
|
@ -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 @@ class AnalysisConsumer : public ASTConsumer,
|
||||
/// 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 @@ class AnalysisConsumer : public ASTConsumer,
|
||||
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 @@ class AnalysisConsumer : public ASTConsumer,
|
||||
|
||||
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 @@ class AnalysisConsumer : public ASTConsumer,
|
||||
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,
|
||||
|
@ -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}}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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 *);
|
||||
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
@ -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}}
|
||||
}
|
||||
|
@ -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
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user