diff options
author | David L. Jones <dlj@google.com> | 2017-11-10 01:07:01 +0000 |
---|---|---|
committer | David L. Jones <dlj@google.com> | 2017-11-10 01:07:01 +0000 |
commit | 41af1698c520ea38edf83e7c91f1e519d34f20c1 (patch) | |
tree | 05c516cb7514d80a5e8deccb07cd0f7c228b70d4 /test/Analysis | |
parent | cd1b175aa96d9d675c09fc54dfd96ba41e3f2279 (diff) | |
parent | 4d085086c74a8fbce197f61548f488a63f300933 (diff) |
Creating branches/google/testing and tags/google/testing/ from r317203
git-svn-id: https://llvm.org/svn/llvm-project/cfe/branches/google/testing@317856 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/Analysis')
114 files changed, 5747 insertions, 3020 deletions
diff --git a/test/Analysis/DeleteWithNonVirtualDtor.cpp b/test/Analysis/DeleteWithNonVirtualDtor.cpp new file mode 100644 index 0000000000..a9b8a11f34 --- /dev/null +++ b/test/Analysis/DeleteWithNonVirtualDtor.cpp @@ -0,0 +1,187 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.cplusplus.DeleteWithNonVirtualDtor -std=c++11 -verify -analyzer-output=text %s + +struct Virtual { + virtual ~Virtual() {} +}; + +struct VDerived : public Virtual {}; + +struct NonVirtual { + ~NonVirtual() {} +}; + +struct NVDerived : public NonVirtual {}; +struct NVDoubleDerived : public NVDerived {}; + +struct Base { + virtual void destroy() = 0; +}; + +class PrivateDtor final : public Base { +public: + void destroy() { delete this; } +private: + ~PrivateDtor() {} +}; + +struct ImplicitNV { + virtual void f(); +}; + +struct ImplicitNVDerived : public ImplicitNV {}; + +NVDerived *get(); + +NonVirtual *create() { + NonVirtual *x = new NVDerived(); // expected-note{{Conversion from derived to base happened here}} + return x; +} + +void sink(NonVirtual *x) { + delete x; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void sinkCast(NonVirtual *y) { + delete reinterpret_cast<NVDerived*>(y); +} + +void sinkParamCast(NVDerived *z) { + delete z; +} + +void singleDerived() { + NonVirtual *sd; + sd = new NVDerived(); // expected-note{{Conversion from derived to base happened here}} + delete sd; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void singleDerivedArr() { + NonVirtual *sda = new NVDerived[5]; // expected-note{{Conversion from derived to base happened here}} + delete[] sda; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void doubleDerived() { + NonVirtual *dd = new NVDoubleDerived(); // expected-note{{Conversion from derived to base happened here}} + delete (dd); // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void assignThroughFunction() { + NonVirtual *atf = get(); // expected-note{{Conversion from derived to base happened here}} + delete atf; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void assignThroughFunction2() { + NonVirtual *atf2; + atf2 = get(); // expected-note{{Conversion from derived to base happened here}} + delete atf2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void createThroughFunction() { + NonVirtual *ctf = create(); // expected-note{{Calling 'create'}} + // expected-note@-1{{Returning from 'create'}} + delete ctf; // expected-warning {{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void deleteThroughFunction() { + NonVirtual *dtf = new NVDerived(); // expected-note{{Conversion from derived to base happened here}} + sink(dtf); // expected-note{{Calling 'sink'}} +} + +void singleCastCStyle() { + NVDerived *sccs = new NVDerived(); + NonVirtual *sccs2 = (NonVirtual*)sccs; // expected-note{{Conversion from derived to base happened here}} + delete sccs2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void doubleCastCStyle() { + NonVirtual *dccs = new NVDerived(); + NVDerived *dccs2 = (NVDerived*)dccs; + dccs = (NonVirtual*)dccs2; // expected-note{{Conversion from derived to base happened here}} + delete dccs; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void singleCast() { + NVDerived *sc = new NVDerived(); + NonVirtual *sc2 = reinterpret_cast<NonVirtual*>(sc); // expected-note{{Conversion from derived to base happened here}} + delete sc2; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void doubleCast() { + NonVirtual *dd = new NVDerived(); + NVDerived *dd2 = reinterpret_cast<NVDerived*>(dd); + dd = reinterpret_cast<NonVirtual*>(dd2); // expected-note {{Conversion from derived to base happened here}} + delete dd; // expected-warning {{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void implicitNV() { + ImplicitNV *invd = new ImplicitNVDerived(); // expected-note{{Conversion from derived to base happened here}} + delete invd; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void doubleDecl() { + ImplicitNV *dd1, *dd2; + dd1 = new ImplicitNVDerived(); // expected-note{{Conversion from derived to base happened here}} + delete dd1; // expected-warning{{Destruction of a polymorphic object with no virtual destructor}} + // expected-note@-1{{Destruction of a polymorphic object with no virtual destructor}} +} + +void virtualBase() { + Virtual *vb = new VDerived(); + delete vb; // no-warning +} + +void notDerived() { + NonVirtual *nd = new NonVirtual(); + delete nd; // no-warning +} + +void notDerivedArr() { + NonVirtual *nda = new NonVirtual[3]; + delete[] nda; // no-warning +} + +void cast() { + NonVirtual *c = new NVDerived(); + delete reinterpret_cast<NVDerived*>(c); // no-warning +} + +void deleteThroughFunction2() { + NonVirtual *dtf2 = new NVDerived(); + sinkCast(dtf2); // no-warning +} + +void deleteThroughFunction3() { + NVDerived *dtf3; + dtf3 = new NVDerived(); + sinkParamCast(dtf3); // no-warning +} + +void stackVar() { + NonVirtual sv2; + delete &sv2; // no-warning +} + +// Deleting a polymorphic object with a non-virtual dtor +// is not a problem if it is referenced by its precise type. + +void preciseType() { + NVDerived *pt = new NVDerived(); + delete pt; // no-warning +} + +void privateDtor() { + Base *pd = new PrivateDtor(); + pd->destroy(); // no-warning +} diff --git a/test/Analysis/DynamicTypePropagation.m b/test/Analysis/DynamicTypePropagation.m index 25a0ae35fd..63904b8425 100644 --- a/test/Analysis/DynamicTypePropagation.m +++ b/test/Analysis/DynamicTypePropagation.m @@ -4,6 +4,9 @@ # error Compiler does not support Objective-C generics? #endif +typedef __typeof(sizeof(int)) size_t; +void *memset(void *, int, size_t); + #define nil 0 typedef unsigned long NSUInteger; typedef int BOOL; @@ -21,6 +24,7 @@ __attribute__((objc_root_class)) @end @interface NSArray<ObjectType> : NSObject +- (void) init; - (BOOL)contains:(ObjectType)obj; - (ObjectType)getObjAtIndex:(NSUInteger)idx; - (ObjectType)objectAtIndexedSubscript:(NSUInteger)idx; @@ -55,3 +59,11 @@ void testArgument(NSArray<MyType *> *arr, id element) { // MyType! [element myFunction:0 myParam:0 ]; } + +// Do not try this at home! The analyzer shouldn't crash though when it +// tries to figure out the dynamic type behind the alloca's return value. +void testAlloca(size_t NSArrayClassSizeWeKnowSomehow) { + NSArray *arr = __builtin_alloca(NSArrayClassSizeWeKnowSomehow); + memset(arr, 0, NSArrayClassSizeWeKnowSomehow); + [arr init]; // no-crash +} diff --git a/test/Analysis/Inputs/system-header-simulator-cxx.h b/test/Analysis/Inputs/system-header-simulator-cxx.h index 005e7f57af..809b768d71 100644 --- a/test/Analysis/Inputs/system-header-simulator-cxx.h +++ b/test/Analysis/Inputs/system-header-simulator-cxx.h @@ -8,18 +8,61 @@ typedef unsigned char uint8_t; typedef __typeof__(sizeof(int)) size_t; +typedef __typeof__((char*)0-(char*)0) ptrdiff_t; void *memmove(void *s1, const void *s2, size_t n); -template <typename T, typename Ptr, typename Ref> struct __iterator { - typedef __iterator<T, T *, T &> iterator; - typedef __iterator<T, const T *, const T &> const_iterator; +namespace std { + struct input_iterator_tag { }; + struct output_iterator_tag { }; + struct forward_iterator_tag : public input_iterator_tag { }; + struct bidirectional_iterator_tag : public forward_iterator_tag { }; + struct random_access_iterator_tag : public bidirectional_iterator_tag { }; - __iterator(const Ptr p) : ptr(p) {} + template <typename Iterator> struct iterator_traits { + typedef typename Iterator::difference_type difference_type; + typedef typename Iterator::value_type value_type; + typedef typename Iterator::pointer pointer; + typedef typename Iterator::reference reference; + typedef typename Iterator::iterator_category iterator_category; + }; +} + +template <typename T, typename Ptr, typename Ref> struct __vector_iterator { + typedef __vector_iterator<T, T *, T &> iterator; + typedef __vector_iterator<T, const T *, const T &> const_iterator; + + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef Ptr pointer; + typedef Ref reference; + typedef std::random_access_iterator_tag iterator_category; + + __vector_iterator(const Ptr p = 0) : ptr(p) {} + __vector_iterator(const iterator &rhs): ptr(rhs.base()) {} + __vector_iterator<T, Ptr, Ref> operator++() { ++ ptr; return *this; } + __vector_iterator<T, Ptr, Ref> operator++(int) { + auto tmp = *this; + ++ ptr; + return tmp; + } + __vector_iterator<T, Ptr, Ref> operator--() { -- ptr; return *this; } + __vector_iterator<T, Ptr, Ref> operator--(int) { + auto tmp = *this; -- ptr; + return tmp; + } + __vector_iterator<T, Ptr, Ref> operator+(difference_type n) { + return ptr + n; + } + __vector_iterator<T, Ptr, Ref> operator-(difference_type n) { + return ptr - n; + } + __vector_iterator<T, Ptr, Ref> operator+=(difference_type n) { + return ptr += n; + } + __vector_iterator<T, Ptr, Ref> operator-=(difference_type n) { + return ptr -= n; + } - __iterator<T, Ptr, Ref> operator++() { return *this; } - __iterator<T, Ptr, Ref> operator++(int) { return *this; } - __iterator<T, Ptr, Ref> operator--() { return *this; } - __iterator<T, Ptr, Ref> operator--(int) { return *this; } Ref operator*() const { return *ptr; } Ptr operator->() const { return *ptr; } @@ -29,10 +72,136 @@ template <typename T, typename Ptr, typename Ref> struct __iterator { bool operator!=(const iterator &rhs) const { return ptr != rhs.ptr; } bool operator!=(const const_iterator &rhs) const { return ptr != rhs.ptr; } + const Ptr& base() const { return ptr; } + private: Ptr ptr; }; +template <typename T, typename Ptr, typename Ref> struct __deque_iterator { + typedef __deque_iterator<T, T *, T &> iterator; + typedef __deque_iterator<T, const T *, const T &> const_iterator; + + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef Ptr pointer; + typedef Ref reference; + typedef std::random_access_iterator_tag iterator_category; + + __deque_iterator(const Ptr p = 0) : ptr(p) {} + __deque_iterator(const iterator &rhs): ptr(rhs.base()) {} + __deque_iterator<T, Ptr, Ref> operator++() { ++ ptr; return *this; } + __deque_iterator<T, Ptr, Ref> operator++(int) { + auto tmp = *this; + ++ ptr; + return tmp; + } + __deque_iterator<T, Ptr, Ref> operator--() { -- ptr; return *this; } + __deque_iterator<T, Ptr, Ref> operator--(int) { + auto tmp = *this; -- ptr; + return tmp; + } + __deque_iterator<T, Ptr, Ref> operator+(difference_type n) { + return ptr + n; + } + __deque_iterator<T, Ptr, Ref> operator-(difference_type n) { + return ptr - n; + } + __deque_iterator<T, Ptr, Ref> operator+=(difference_type n) { + return ptr += n; + } + __deque_iterator<T, Ptr, Ref> operator-=(difference_type n) { + return ptr -= n; + } + + Ref operator*() const { return *ptr; } + Ptr operator->() const { return *ptr; } + + bool operator==(const iterator &rhs) const { return ptr == rhs.ptr; } + bool operator==(const const_iterator &rhs) const { return ptr == rhs.ptr; } + + bool operator!=(const iterator &rhs) const { return ptr != rhs.ptr; } + bool operator!=(const const_iterator &rhs) const { return ptr != rhs.ptr; } + + const Ptr& base() const { return ptr; } + +private: + Ptr ptr; +}; + +template <typename T, typename Ptr, typename Ref> struct __list_iterator { + typedef __list_iterator<T, __typeof__(T::data) *, __typeof__(T::data) &> iterator; + typedef __list_iterator<T, const __typeof__(T::data) *, const __typeof__(T::data) &> const_iterator; + + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef Ptr pointer; + typedef Ref reference; + typedef std::bidirectional_iterator_tag iterator_category; + + __list_iterator(T* it = 0) : item(it) {} + __list_iterator(const iterator &rhs): item(rhs.base()) {} + __list_iterator<T, Ptr, Ref> operator++() { item = item->next; return *this; } + __list_iterator<T, Ptr, Ref> operator++(int) { + auto tmp = *this; + item = item->next; + return tmp; + } + __list_iterator<T, Ptr, Ref> operator--() { item = item->prev; return *this; } + __list_iterator<T, Ptr, Ref> operator--(int) { + auto tmp = *this; + item = item->prev; + return tmp; + } + + Ref operator*() const { return item->data; } + Ptr operator->() const { return item->data; } + + bool operator==(const iterator &rhs) const { return item == rhs->item; } + bool operator==(const const_iterator &rhs) const { return item == rhs->item; } + + bool operator!=(const iterator &rhs) const { return item != rhs->item; } + bool operator!=(const const_iterator &rhs) const { return item != rhs->item; } + + const T* &base() const { return item; } + +private: + T* item; +}; + +template <typename T, typename Ptr, typename Ref> struct __fwdl_iterator { + typedef __fwdl_iterator<T, __typeof__(T::data) *, __typeof__(T::data) &> iterator; + typedef __fwdl_iterator<T, const __typeof__(T::data) *, const __typeof__(T::data) &> const_iterator; + + typedef ptrdiff_t difference_type; + typedef T value_type; + typedef Ptr pointer; + typedef Ref reference; + typedef std::forward_iterator_tag iterator_category; + + __fwdl_iterator(T* it = 0) : item(it) {} + __fwdl_iterator(const iterator &rhs): item(rhs.base()) {} + __fwdl_iterator<T, Ptr, Ref> operator++() { item = item->next; return *this; } + __fwdl_iterator<T, Ptr, Ref> operator++(int) { + auto tmp = *this; + item = item->next; + return tmp; + } + Ref operator*() const { return item->data; } + Ptr operator->() const { return item->data; } + + bool operator==(const iterator &rhs) const { return item == rhs->item; } + bool operator==(const const_iterator &rhs) const { return item == rhs->item; } + + bool operator!=(const iterator &rhs) const { return item != rhs->item; } + bool operator!=(const const_iterator &rhs) const { return item != rhs->item; } + + const T* &base() const { return item; } + +private: + T* item; +}; + namespace std { template <class T1, class T2> struct pair { @@ -43,30 +212,124 @@ namespace std { pair(const T1 &a, const T2 &b) : first(a), second(b) {} template<class U1, class U2> - pair(const pair<U1, U2> &other) : first(other.first), second(other.second) {} + pair(const pair<U1, U2> &other) : first(other.first), + second(other.second) {} }; typedef __typeof__(sizeof(int)) size_t; + + template <class T> class initializer_list; + template< class T > struct remove_reference {typedef T type;}; + template< class T > struct remove_reference<T&> {typedef T type;}; + template< class T > struct remove_reference<T&&> {typedef T type;}; + + template<class T> + typename remove_reference<T>::type&& move(T&& a) { + typedef typename remove_reference<T>::type&& RvalRef; + return static_cast<RvalRef>(a); + } + template<typename T> class vector { - typedef __iterator<T, T *, T &> iterator; - typedef __iterator<T, const T *, const T &> const_iterator; + typedef T value_type; + typedef size_t size_type; + typedef __vector_iterator<T, T *, T &> iterator; + typedef __vector_iterator<T, const T *, const T &> const_iterator; T *_start; T *_finish; T *_end_of_storage; public: vector() : _start(0), _finish(0), _end_of_storage(0) {} + template <typename InputIterator> + vector(InputIterator first, InputIterator last); + vector(const vector &other); + vector(vector &&other); ~vector(); size_t size() const { return size_t(_finish - _start); } + + T &operator[](size_t n) { + return _start[n]; + } + + const T &operator[](size_t n) const { + return _start[n]; + } + + iterator begin() { return iterator(_start); } + const_iterator begin() const { return const_iterator(_start); } + const_iterator cbegin() const { return const_iterator(_start); } + iterator end() { return iterator(_finish); } + const_iterator end() const { return const_iterator(_finish); } + const_iterator cend() const { return const_iterator(_finish); } + T& front() { return *begin(); } + const T& front() const { return *begin(); } + T& back() { return *(end() - 1); } + const T& back() const { return *(end() - 1); } + }; + + template<typename T> + class list { + struct __item { + T data; + __item *prev, *next; + } *_start, *_finish; + public: + typedef T value_type; + typedef size_t size_type; + typedef __list_iterator<__item, T *, T &> iterator; + typedef __list_iterator<__item, const T *, const T &> const_iterator; + + list() : _start(0), _finish(0) {} + template <typename InputIterator> + list(InputIterator first, InputIterator last); + list(const list &other); + list(list &&other); + ~list(); - void push_back(); - T pop_back(); + list& operator=(const list &other); + list& operator=(list &&other); + list& operator=(std::initializer_list<T> ilist); + + iterator begin() { return iterator(_start); } + const_iterator begin() const { return const_iterator(_start); } + const_iterator cbegin() const { return const_iterator(_start); } + iterator end() { return iterator(_finish); } + const_iterator end() const { return const_iterator(_finish); } + const_iterator cend() const { return const_iterator(_finish); } + + T& front() { return *begin(); } + const T& front() const { return *begin(); } + T& back() { return *--end(); } + const T& back() const { return *--end(); } + }; + template<typename T> + class deque { + typedef T value_type; + typedef size_t size_type; + typedef __deque_iterator<T, T *, T &> iterator; + typedef __deque_iterator<T, const T *, const T &> const_iterator; + + T *_start; + T *_finish; + T *_end_of_storage; + public: + deque() : _start(0), _finish(0), _end_of_storage(0) {} + template <typename InputIterator> + deque(InputIterator first, InputIterator last); + deque(const deque &other); + deque(deque &&other); + ~deque(); + + size_t size() const { + return size_t(_finish - _start); + } + T &operator[](size_t n) { return _start[n]; } @@ -77,10 +340,46 @@ namespace std { iterator begin() { return iterator(_start); } const_iterator begin() const { return const_iterator(_start); } + const_iterator cbegin() const { return const_iterator(_start); } iterator end() { return iterator(_finish); } const_iterator end() const { return const_iterator(_finish); } + const_iterator cend() const { return const_iterator(_finish); } + T& front() { return *begin(); } + const T& front() const { return *begin(); } + T& back() { return *(end() - 1); } + const T& back() const { return *(end() - 1); } }; + template<typename T> + class forward_list { + struct __item { + T data; + __item *next; + } *_start; + public: + typedef T value_type; + typedef size_t size_type; + typedef __fwdl_iterator<__item, T *, T &> iterator; + typedef __fwdl_iterator<__item, const T *, const T &> const_iterator; + + forward_list() : _start(0) {} + template <typename InputIterator> + forward_list(InputIterator first, InputIterator last); + forward_list(const forward_list &other); + forward_list(forward_list &&other); + ~forward_list(); + + iterator begin() { return iterator(_start); } + const_iterator begin() const { return const_iterator(_start); } + const_iterator cbegin() const { return const_iterator(_start); } + iterator end() { return iterator(); } + const_iterator end() const { return const_iterator(); } + const_iterator cend() const { return const_iterator(); } + + T& front() { return *begin(); } + const T& front() const { return *begin(); } + }; + class exception { public: exception() throw(); @@ -247,41 +546,41 @@ namespace std { OutputIter copy_backward(InputIter II, InputIter IE, OutputIter OI) { return __copy_backward(II, IE, OI); } +} + +template <class BidirectionalIterator, class Distance> +void __advance (BidirectionalIterator& it, Distance n, + std::bidirectional_iterator_tag) { + if (n >= 0) while(n-- > 0) ++it; else while (n++<0) --it; +} + +template <class RandomAccessIterator, class Distance> +void __advance (RandomAccessIterator& it, Distance n, + std::random_access_iterator_tag) { + it += n; +} + +namespace std { + template <class InputIterator, class Distance> + void advance (InputIterator& it, Distance n) { + __advance(it, n, typename InputIterator::iterator_category()); + } + + template <class BidirectionalIterator> + BidirectionalIterator + prev (BidirectionalIterator it, + typename iterator_traits<BidirectionalIterator>::difference_type n = + 1) { + advance(it, -n); + return it; + } template <class InputIterator, class T> InputIterator find(InputIterator first, InputIterator last, const T &val); - template <class ForwardIterator1, class ForwardIterator2> - ForwardIterator1 find_end(ForwardIterator1 first1, ForwardIterator1 last1, - ForwardIterator2 first2, ForwardIterator2 last2); - template <class ForwardIterator1, class ForwardIterator2> - ForwardIterator1 find_first_of(ForwardIterator1 first1, - ForwardIterator1 last1, - ForwardIterator2 first2, - ForwardIterator2 last2); - template <class InputIterator, class UnaryPredicate> - InputIterator find_if(InputIterator first, InputIterator last, - UnaryPredicate pred); - template <class InputIterator, class UnaryPredicate> - InputIterator find_if_not(InputIterator first, InputIterator last, - UnaryPredicate pred); - template <class InputIterator, class T> - InputIterator lower_bound(InputIterator first, InputIterator last, - const T &val); - template <class InputIterator, class T> - InputIterator upper_bound(InputIterator first, InputIterator last, - const T &val); - template <class ForwardIterator1, class ForwardIterator2> - ForwardIterator1 search(ForwardIterator1 first1, ForwardIterator1 last1, - ForwardIterator2 first2, ForwardIterator2 last2); - template <class ForwardIterator1, class ForwardIterator2> - ForwardIterator1 search_n(ForwardIterator1 first1, ForwardIterator1 last1, - ForwardIterator2 first2, ForwardIterator2 last2); - struct input_iterator_tag { }; - struct output_iterator_tag { }; - struct forward_iterator_tag : public input_iterator_tag { }; - struct bidirectional_iterator_tag : public forward_iterator_tag { }; - struct random_access_iterator_tag : public bidirectional_iterator_tag { }; + template <class InputIterator, class OutputIterator> + OutputIterator copy(InputIterator first, InputIterator last, + OutputIterator result); } diff --git a/test/Analysis/MismatchedDeallocator-path-notes.cpp b/test/Analysis/MismatchedDeallocator-path-notes.cpp index 118f23bca1..1354386fc8 100644 --- a/test/Analysis/MismatchedDeallocator-path-notes.cpp +++ b/test/Analysis/MismatchedDeallocator-path-notes.cpp @@ -287,7 +287,7 @@ void test() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Bad deallocator</string> // CHECK-NEXT: <key>check_name</key><string>unix.MismatchedDeallocator</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> diff --git a/test/Analysis/MisusedMovedObject.cpp b/test/Analysis/MisusedMovedObject.cpp new file mode 100644 index 0000000000..132a65de10 --- /dev/null +++ b/test/Analysis/MisusedMovedObject.cpp @@ -0,0 +1,675 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.cplusplus.MisusedMovedObject -std=c++11 -verify -analyzer-output=text %s + +namespace std { + +template <typename> +struct remove_reference; + +template <typename _Tp> +struct remove_reference { typedef _Tp type; }; + +template <typename _Tp> +struct remove_reference<_Tp &> { typedef _Tp type; }; + +template <typename _Tp> +struct remove_reference<_Tp &&> { typedef _Tp type; }; + +template <typename _Tp> +typename remove_reference<_Tp>::type &&move(_Tp &&__t) { + return static_cast<typename remove_reference<_Tp>::type &&>(__t); +} + +template <typename _Tp> +_Tp &&forward(typename remove_reference<_Tp>::type &__t) noexcept { + return static_cast<_Tp &&>(__t); +} + +template <class T> +void swap(T &a, T &b) { + T c(std::move(a)); + a = std::move(b); + b = std::move(c); +} + +} // namespace std + +class B { +public: + B() = default; + B(const B &) = default; + B(B &&) = default; + B& operator=(const B &q) = default; + void operator=(B &&b) { + return; + } + void foo() { return; } +}; + +class A { + int i; + double d; + +public: + B b; + A(int ii = 42, double dd = 1.0) : d(dd), i(ii), b(B()) {} + void moveconstruct(A &&other) { + std::swap(b, other.b); + std::swap(d, other.d); + std::swap(i, other.i); + return; + } + static A get() { + A v(12, 13); + return v; + } + A(A *a) { + moveconstruct(std::move(*a)); + } + A(const A &other) : i(other.i), d(other.d), b(other.b) {} + A(A &&other) : i(other.i), d(other.d), b(std::move(other.b)) { // expected-note {{'b' became 'moved-from' here}} + } + A(A &&other, char *k) { + moveconstruct(std::move(other)); + } + void operator=(const A &other) { + i = other.i; + d = other.d; + b = other.b; + return; + } + void operator=(A &&other) { + moveconstruct(std::move(other)); + return; + } + int getI() { return i; } + int foo() const; + void bar() const; + void reset(); + void destroy(); + void clear(); + bool empty() const; + bool isEmpty() const; + operator bool() const; +}; + +int bignum(); + +void moveInsideFunctionCall(A a) { + A b = std::move(a); +} +void leftRefCall(A &a) { + a.foo(); +} +void rightRefCall(A &&a) { + a.foo(); +} +void constCopyOrMoveCall(const A a) { + a.foo(); +} + +void copyOrMoveCall(A a) { + a.foo(); +} + +void simpleMoveCtorTest() { + { + A a; + A b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} + } + { + A a; + A b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + b = a; // expected-warning {{Copying a 'moved-from' object 'a'}} expected-note {{Copying a 'moved-from' object 'a'}} + } + { + A a; + A b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + b = std::move(a); // expected-warning {{Moving a 'moved-from' object 'a'}} expected-note {{Moving a 'moved-from' object 'a'}} + } +} + +void simpleMoveAssignementTest() { + { + A a; + A b; + b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} + } + { + A a; + A b; + b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + A c(a); // expected-warning {{Copying a 'moved-from' object 'a'}} expected-note {{Copying a 'moved-from' object 'a'}} + } + { + A a; + A b; + b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + A c(std::move(a)); // expected-warning {{Moving a 'moved-from' object 'a'}} expected-note {{Moving a 'moved-from' object 'a'}} + } +} + +void moveInInitListTest() { + struct S { + A a; + }; + A a; + S s{std::move(a)}; // expected-note {{'a' became 'moved-from' here}} + a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} +} + +// Don't report a bug if the variable was assigned to in the meantime. +void reinitializationTest(int i) { + { + A a; + A b; + b = std::move(a); + a = A(); + a.foo(); + } + { + A a; + if (i == 1) { // expected-note {{Assuming 'i' is not equal to 1}} expected-note {{Taking false branch}} + // expected-note@-1 {{Assuming 'i' is not equal to 1}} expected-note@-1 {{Taking false branch}} + A b; + b = std::move(a); + a = A(); + } + if (i == 2) { // expected-note {{Assuming 'i' is not equal to 2}} expected-note {{Taking false branch}} + //expected-note@-1 {{Assuming 'i' is not equal to 2}} expected-note@-1 {{Taking false branch}} + a.foo(); // no-warning + } + } + { + A a; + if (i == 1) { // expected-note {{Taking false branch}} expected-note {{Taking false branch}} + std::move(a); + } + if (i == 2) { // expected-note {{Taking false branch}} expected-note {{Taking false branch}} + a = A(); + a.foo(); + } + } + // The built-in assignment operator should also be recognized as a + // reinitialization. (std::move() may be called on built-in types in template + // code.) + { + int a1 = 1, a2 = 2; + std::swap(a1, a2); + } + // A std::move() after the assignment makes the variable invalid again. + { + A a; + A b; + b = std::move(a); + a = A(); + b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} + } + // If a path exist where we not reinitialize the variable we report a bug. + { + A a; + A b; + b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + if (i < 10) { // expected-note {{Assuming 'i' is >= 10}} expected-note {{Taking false branch}} + a = A(); + } + if (i > 5) { // expected-note {{Taking true branch}} + a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} + } + } +} + +// Using decltype on an expression is not a use. +void decltypeIsNotUseTest() { + A a; + // A b(std::move(a)); + decltype(a) other_a; // no-warning +} + +void loopTest() { + { + A a; + for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}} + rightRefCall(std::move(a)); // no-warning + } + } + { + A a; + for (int i = 0; i < 2; i++) { // expected-note {{Loop condition is true. Entering loop body}} + //expected-note@-1 {{Loop condition is true. Entering loop body}} + //expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}} + rightRefCall(std::move(a)); // no-warning + } + } + { + A a; + for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}} + leftRefCall(a); // no-warning + } + } + { + A a; + for (int i = 0; i < 2; i++) { // expected-note {{Loop condition is true. Entering loop body}} + //expected-note@-1 {{Loop condition is true. Entering loop body}} + //expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}} + leftRefCall(a); // no-warning + } + } + { + A a; + for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}} + constCopyOrMoveCall(a); // no-warning + } + } + { + A a; + for (int i = 0; i < 2; i++) { // expected-note {{Loop condition is true. Entering loop body}} + //expected-note@-1 {{Loop condition is true. Entering loop body}} + //expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}} + constCopyOrMoveCall(a); // no-warning + } + } + { + A a; + for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}} + moveInsideFunctionCall(a); // no-warning + } + } + { + A a; + for (int i = 0; i < 2; i++) { // expected-note {{Loop condition is true. Entering loop body}} + //expected-note@-1 {{Loop condition is true. Entering loop body}} + //expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}} + moveInsideFunctionCall(a); // no-warning + } + } + { + A a; + for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is false. Execution jumps to the end of the function}} + copyOrMoveCall(a); // no-warning + } + } + { + A a; + for (int i = 0; i < 2; i++) { // expected-note {{Loop condition is true.}} + //expected-note@-1 {{Loop condition is true. Entering loop body}} + //expected-note@-2 {{Loop condition is false. Execution jumps to the end of the function}} + copyOrMoveCall(a); // no-warning + } + } + { + A a; + for (int i = 0; i < bignum(); i++) { // expected-note {{Loop condition is true. Entering loop body}} expected-note {{Loop condition is true. Entering loop body}} + constCopyOrMoveCall(std::move(a)); // expected-warning {{Moving a 'moved-from' object 'a'}} expected-note {{Moving a 'moved-from' object 'a'}} + // expected-note@-1 {{'a' became 'moved-from' here}} + } + } + + // Don't warn if we return after the move. + { + A a; + for (int i = 0; i < 3; ++i) { + a.bar(); + if (a.foo() > 0) { + A b; + b = std::move(a); // no-warning + return; + } + } + } +} + +//report a usage of a moved-from object only at the first use +void uniqueTest(bool cond) { + A a(42, 42.0); + A b; + b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + + if (cond) { // expected-note {{Assuming 'cond' is not equal to 0}} expected-note {{Taking true branch}} + a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} + } + if (cond) { + a.bar(); // no-warning + } + + a.bar(); // no-warning +} + +void uniqueTest2() { + A a; + A a1 = std::move(a); // expected-note {{'a' became 'moved-from' here}} + a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} + + A a2 = std::move(a); // no-warning + a.foo(); // no-warning +} + +// There are exceptions where we assume in general that the method works fine +//even on moved-from objects. +void moveSafeFunctionsTest() { + A a; + A b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + a.empty(); // no-warning + a.isEmpty(); // no-warning + (void)a; // no-warning + (bool)a; // expected-warning {{expression result unused}} + a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} +} + +void moveStateResetFunctionsTest() { + { + A a; + A b = std::move(a); + a.reset(); // no-warning + a.foo(); // no-warning + // Test if resets the state of subregions as well. + a.b.foo(); // no-warning + } + { + A a; + A b = std::move(a); + a.destroy(); // no-warning + a.foo(); // no-warning + } + { + A a; + A b = std::move(a); + a.clear(); // no-warning + a.foo(); // no-warning + a.b.foo(); // no-warning + } +} + +// Moves or uses that occur as part of template arguments. +template <int> +class ClassTemplate { +public: + void foo(A a); +}; + +template <int> +void functionTemplate(A a); + +void templateArgIsNotUseTest() { + { + // A pattern like this occurs in the EXPECT_EQ and ASSERT_EQ macros in + // Google Test. + A a; + ClassTemplate<sizeof(A(std::move(a)))>().foo(std::move(a)); // no-warning + } + { + A a; + functionTemplate<sizeof(A(std::move(a)))>(std::move(a)); // no-warning + } +} + +// Moves of global variables are not reported. +A global_a; +void globalVariablesTest() { + std::move(global_a); + global_a.foo(); // no-warning +} + +// Moves of member variables. +class memberVariablesTest { + A a; + static A static_a; + + void f() { + A b; + b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + a.foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object 'a'}} + + b = std::move(static_a); // expected-note {{'static_a' became 'moved-from' here}} + static_a.foo(); // expected-warning {{Method call on a 'moved-from' object 'static_a'}} expected-note {{Method call on a 'moved-from' object 'static_a'}} + } +}; + +void PtrAndArrayTest() { + A *Ptr = new A(1, 1.5); + A Arr[10]; + Arr[2] = std::move(*Ptr); // expected-note {{Became 'moved-from' here}} + (*Ptr).foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object}} + + Ptr = &Arr[1]; + Arr[3] = std::move(Arr[1]); // expected-note {{Became 'moved-from' here}} + Ptr->foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object}} + + Arr[3] = std::move(Arr[2]); // expected-note {{Became 'moved-from' here}} + Arr[2].foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object}} + + Arr[2] = std::move(Arr[3]); // reinitialization + Arr[2].foo(); // no-warning +} + +void exclusiveConditionsTest(bool cond) { + A a; + if (cond) { + A b; + b = std::move(a); + } + if (!cond) { + a.bar(); // no-warning + } +} + +void differentBranchesTest(int i) { + // Don't warn if the use is in a different branch from the move. + { + A a; + if (i > 0) { // expected-note {{Assuming 'i' is > 0}} expected-note {{Taking true branch}} + A b; + b = std::move(a); + } else { + a.foo(); // no-warning + } + } + // Same thing, but with a ternary operator. + { + A a, b; + i > 0 ? (void)(b = std::move(a)) : a.bar(); // no-warning // expected-note {{'?' condition is true}} + } + // A variation on the theme above. + { + A a; + a.foo() > 0 ? a.foo() : A(std::move(a)).foo(); // expected-note {{Assuming the condition is false}} expected-note {{'?' condition is false}} + } + // Same thing, but with a switch statement. + { + A a, b; + switch (i) { // expected-note {{Control jumps to 'case 1:' at line 483}} + case 1: + b = std::move(a); // no-warning + break; // expected-note {{Execution jumps to the end of the function}} + case 2: + a.foo(); // no-warning + break; + } + } + // However, if there's a fallthrough, we do warn. + { + A a, b; + switch (i) { // expected-note {{Control jumps to 'case 1:' at line 495}} + case 1: + b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + case 2: + a.foo(); // expected-warning {{Method call on a 'moved-from' object}} expected-note {{Method call on a 'moved-from' object 'a'}} + break; + } + } +} + +void tempTest() { + A a = A::get(); + A::get().foo(); // no-warning + for (int i = 0; i < bignum(); i++) { + A::get().foo(); // no-warning + } +} + +void interFunTest1(A &a) { + a.bar(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} +} + +void interFunTest2() { + A a; + A b; + b = std::move(a); // expected-note {{'a' became 'moved-from' here}} + interFunTest1(a); // expected-note {{Calling 'interFunTest1'}} +} + +void foobar(A a, int i); +void foobar(int i, A a); + +void paramEvaluateOrderTest() { + A a; + foobar(std::move(a), a.getI()); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} + // expected-note@-1 {{'a' became 'moved-from' here}} + + //FALSE NEGATIVE since parameters evaluate order is undefined + foobar(a.getI(), std::move(a)); //no-warning +} + +void not_known(A &a); +void not_known(A *a); + +void regionAndPointerEscapeTest() { + { + A a; + A b; + b = std::move(a); + not_known(a); + a.foo(); //no-warning + } + { + A a; + A b; + b = std::move(a); + not_known(&a); + a.foo(); // no-warning + } +} + +// A declaration statement containing multiple declarations sequences the +// initializer expressions. +void declarationSequenceTest() { + { + A a; + A a1 = a, a2 = std::move(a); // no-warning + } + { + A a; + A a1 = std::move(a), a2 = a; // expected-warning {{Copying a 'moved-from' object 'a'}} expected-note {{Copying a 'moved-from' object 'a'}} + // expected-note@-1 {{'a' became 'moved-from' here}} + } +} + +// The logical operators && and || sequence their operands. +void logicalOperatorsSequenceTest() { + { + A a; + if (a.foo() > 0 && A(std::move(a)).foo() > 0) { // expected-note {{Assuming the condition is false}} expected-note {{Assuming the condition is false}} + // expected-note@-1 {{Left side of '&&' is false}} expected-note@-1 {{Left side of '&&' is false}} + //expected-note@-2 {{Taking false branch}} expected-note@-2 {{Taking false branch}} + A().bar(); + } + } + // A variation: Negate the result of the && (which pushes the && further down + // into the AST). + { + A a; + if (!(a.foo() > 0 && A(std::move(a)).foo() > 0)) { // expected-note {{Assuming the condition is false}} expected-note {{Assuming the condition is false}} + // expected-note@-1 {{Left side of '&&' is false}} expected-note@-1 {{Left side of '&&' is false}} + // expected-note@-2 {{Taking true branch}} expected-note@-2 {{Taking true branch}} + A().bar(); + } + } + { + A a; + if (A(std::move(a)).foo() > 0 && a.foo() > 0) { // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} + // expected-note@-1 {{'a' became 'moved-from' here}} expected-note@-1 {{Assuming the condition is true}} expected-note@-1 {{Assuming the condition is false}} + // expected-note@-2 {{Left side of '&&' is false}} expected-note@-2 {{Left side of '&&' is true}} + // expected-note@-3 {{Taking false branch}} + A().bar(); + } + } + { + A a; + if (a.foo() > 0 || A(std::move(a)).foo() > 0) { // expected-note {{Assuming the condition is true}} + //expected-note@-1 {{Left side of '||' is true}} + //expected-note@-2 {{Taking true branch}} + A().bar(); + } + } + { + A a; + if (A(std::move(a)).foo() > 0 || a.foo() > 0) { // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} + // expected-note@-1 {{'a' became 'moved-from' here}} expected-note@-1 {{Assuming the condition is false}} expected-note@-1 {{Left side of '||' is false}} + A().bar(); + } + } +} + +// A range-based for sequences the loop variable declaration before the body. +void forRangeSequencesTest() { + A v[2] = {A(), A()}; + for (A &a : v) { + A b; + b = std::move(a); // no-warning + } +} + +// If a variable is declared in an if statement, the declaration of the variable +// (which is treated like a reinitialization by the check) is sequenced before +// the evaluation of the condition (which constitutes a use). +void ifStmtSequencesDeclAndConditionTest() { + for (int i = 0; i < 3; ++i) { + if (A a = A()) { + A b; + b = std::move(a); // no-warning + } + } +} + +class C : public A {}; +void subRegionMoveTest() { + { + A a; + B b = std::move(a.b); // expected-note {{'b' became 'moved-from' here}} + a.b.foo(); // expected-warning {{Method call on a 'moved-from' object 'b'}} expected-note {{Method call on a 'moved-from' object 'b'}} + } + { + A a; + A a1 = std::move(a); // expected-note {{Calling move constructor for 'A'}} expected-note {{Returning from move constructor for 'A'}} + a.b.foo(); // expected-warning {{Method call on a 'moved-from' object 'b'}} expected-note {{Method call on a 'moved-from' object 'b'}} + } + // Don't report a misuse if any SuperRegion is already reported. + { + A a; + A a1 = std::move(a); // expected-note {{'a' became 'moved-from' here}} + a.foo(); // expected-warning {{Method call on a 'moved-from' object 'a'}} expected-note {{Method call on a 'moved-from' object 'a'}} + a.b.foo(); // no-warning + } + { + C c; + C c1 = std::move(c); // expected-note {{'c' became 'moved-from' here}} + c.foo(); // expected-warning {{Method call on a 'moved-from' object 'c'}} expected-note {{Method call on a 'moved-from' object 'c'}} + c.b.foo(); // no-warning + } +} + +void resetSuperClass() { + C c; + C c1 = std::move(c); + c.clear(); + C c2 = c; // no-warning +} + +void reportSuperClass() { + C c; + C c1 = std::move(c); // expected-note {{'c' became 'moved-from' here}} + c.foo(); // expected-warning {{Method call on a 'moved-from' object 'c'}} expected-note {{Method call on a 'moved-from' object 'c'}} + C c2 = c; // no-warning +} diff --git a/test/Analysis/NewDelete-path-notes.cpp b/test/Analysis/NewDelete-path-notes.cpp index 115a4addca..ac760ca60e 100644 --- a/test/Analysis/NewDelete-path-notes.cpp +++ b/test/Analysis/NewDelete-path-notes.cpp @@ -257,7 +257,7 @@ void test(Odd *odd) { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Attempt to free released memory</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Double free</string> // CHECK-NEXT: <key>check_name</key><string>cplusplus.NewDelete</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -475,7 +475,7 @@ void test(Odd *odd) { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Attempt to free released memory</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Double free</string> // CHECK-NEXT: <key>check_name</key><string>cplusplus.NewDelete</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> diff --git a/test/Analysis/additive-folding.cpp b/test/Analysis/additive-folding.cpp index 52056d472b..823b176004 100644 --- a/test/Analysis/additive-folding.cpp +++ b/test/Analysis/additive-folding.cpp @@ -205,3 +205,12 @@ void multiplicativeSanityTest(int x) { clang_analyzer_eval(x == 3); // expected-warning{{UNKNOWN}} } + +void additiveSymSymFolding(int x, int y) { + // We should simplify 'x - 1' to '0' and handle the comparison, + // despite both sides being complicated symbols. + int z = x - 1; + if (x == 1) + if (y >= 0) + clang_analyzer_eval(z <= y); // expected-warning{{TRUE}} +} diff --git a/test/Analysis/analyzer-config.c b/test/Analysis/analyzer-config.c index c0153a5053..4daea898ec 100644 --- a/test/Analysis/analyzer-config.c +++ b/test/Analysis/analyzer-config.c @@ -12,6 +12,9 @@ void foo() { // CHECK: [config] // CHECK-NEXT: cfg-conditional-static-initializers = true +// CHECK-NEXT: cfg-implicit-dtors = true +// CHECK-NEXT: cfg-lifetime = false +// CHECK-NEXT: cfg-loopexit = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -19,13 +22,13 @@ void foo() { // CHECK-NEXT: ipa = dynamic-bifurcate // CHECK-NEXT: ipa-always-inline-size = 3 // CHECK-NEXT: leak-diagnostics-reference-allocation = false -// CHECK-NEXT: max-inlinable-size = 50 -// CHECK-NEXT: max-nodes = 150000 +// CHECK-NEXT: max-inlinable-size = 100 +// CHECK-NEXT: max-nodes = 225000 // CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep // CHECK-NEXT: region-store-small-struct-limit = 2 +// CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 15 - +// CHECK-NEXT: num-entries = 19 diff --git a/test/Analysis/analyzer-config.cpp b/test/Analysis/analyzer-config.cpp index f84be17811..a08e85e53e 100644 --- a/test/Analysis/analyzer-config.cpp +++ b/test/Analysis/analyzer-config.cpp @@ -23,6 +23,9 @@ public: // CHECK-NEXT: c++-stdlib-inlining = true // CHECK-NEXT: c++-template-inlining = true // CHECK-NEXT: cfg-conditional-static-initializers = true +// CHECK-NEXT: cfg-implicit-dtors = true +// CHECK-NEXT: cfg-lifetime = false +// CHECK-NEXT: cfg-loopexit = false // CHECK-NEXT: cfg-temporary-dtors = false // CHECK-NEXT: faux-bodies = true // CHECK-NEXT: graph-trim-interval = 1000 @@ -30,12 +33,13 @@ public: // CHECK-NEXT: ipa = dynamic-bifurcate // CHECK-NEXT: ipa-always-inline-size = 3 // CHECK-NEXT: leak-diagnostics-reference-allocation = false -// CHECK-NEXT: max-inlinable-size = 50 -// CHECK-NEXT: max-nodes = 150000 +// CHECK-NEXT: max-inlinable-size = 100 +// CHECK-NEXT: max-nodes = 225000 // CHECK-NEXT: max-times-inline-large = 32 // CHECK-NEXT: min-cfg-size-treat-functions-as-large = 14 // CHECK-NEXT: mode = deep // CHECK-NEXT: region-store-small-struct-limit = 2 +// CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 20 +// CHECK-NEXT: num-entries = 24 diff --git a/test/Analysis/analyzer_test.py b/test/Analysis/analyzer_test.py new file mode 100644 index 0000000000..0aa2dbc1bb --- /dev/null +++ b/test/Analysis/analyzer_test.py @@ -0,0 +1,43 @@ +import lit.formats +import lit.TestRunner + +# Custom format class for static analyzer tests +class AnalyzerTest(lit.formats.ShTest): + + def execute(self, test, litConfig): + results = [] + + # Parse any test requirements ('REQUIRES: ') + saved_test = test + lit.TestRunner.parseIntegratedTestScript(test) + + if 'z3' not in test.requires: + results.append(self.executeWithAnalyzeSubstitution( + saved_test, litConfig, '-analyzer-constraints=range')) + + if results[-1].code == lit.Test.FAIL: + return results[-1] + + # If z3 backend available, add an additional run line for it + if test.config.clang_staticanalyzer_z3 == '1': + results.append(self.executeWithAnalyzeSubstitution( + saved_test, litConfig, '-analyzer-constraints=z3 -DANALYZER_CM_Z3')) + + # Combine all result outputs into the last element + for x in results: + if x != results[-1]: + results[-1].output = x.output + results[-1].output + + if results: + return results[-1] + return lit.Test.Result(lit.Test.UNSUPPORTED, + "Test requires the following unavailable features: z3") + + def executeWithAnalyzeSubstitution(self, test, litConfig, substitution): + saved_substitutions = list(test.config.substitutions) + test.config.substitutions.append(('%analyze', substitution)) + result = lit.TestRunner.executeShTest(test, litConfig, + self.execute_external) + test.config.substitutions = saved_substitutions + + return result diff --git a/test/Analysis/bitwise-ops.c b/test/Analysis/bitwise-ops.c index 407aa19289..fe546580be 100644 --- a/test/Analysis/bitwise-ops.c +++ b/test/Analysis/bitwise-ops.c @@ -22,11 +22,32 @@ int testConstantShifts_PR18073(int which) { case 1: return 0ULL << 63; // no-warning case 2: - return 0ULL << 64; // expected-warning{{The result of the '<<' expression is undefined}} + return 0ULL << 64; // expected-warning{{The result of the left shift is undefined due to shifting by '64', which is greater or equal to the width of type 'unsigned long long'}} case 3: - return 0ULL << 65; // expected-warning{{The result of the '<<' expression is undefined}} + return 0ULL << 65; // expected-warning{{The result of the left shift is undefined due to shifting by '65', which is greater or equal to the width of type 'unsigned long long'}} default: return 0; } -}
\ No newline at end of file +} + +int testOverflowShift(int a) { + if (a == 323) { + return 1 << a; // expected-warning{{The result of the left shift is undefined due to shifting by '323', which is greater or equal to the width of type 'int'}} + } + return 0; +} + +int testNegativeShift(int a) { + if (a == -5) { + return 1 << a; // expected-warning{{The result of the left shift is undefined because the right operand is negative}} + } + return 0; +} + +int testNegativeLeftShift(int a) { + if (a == -3) { + return a << 1; // expected-warning{{The result of the left shift is undefined because the left operand is negative}} + } + return 0; +} diff --git a/test/Analysis/block-in-critical-section.cpp b/test/Analysis/block-in-critical-section.cpp index c65cc612cf..fcf6188fc0 100644 --- a/test/Analysis/block-in-critical-section.cpp +++ b/test/Analysis/block-in-critical-section.cpp @@ -7,6 +7,20 @@ struct mutex { void lock() {} void unlock() {} }; +template<typename T> +struct lock_guard { + lock_guard<T>(std::mutex) {} + ~lock_guard<T>() {} +}; +template<typename T> +struct unique_lock { + unique_lock<T>(std::mutex) {} + ~unique_lock<T>() {} +}; +template<typename T> +struct not_real_lock { + not_real_lock<T>(std::mutex) {} +}; } void getc() {} @@ -110,3 +124,31 @@ void testBlockInCriticalSectionUnexpectedUnlock() { m.lock(); sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}} } + +void testBlockInCriticalSectionLockGuard() { + std::mutex g_mutex; + std::not_real_lock<std::mutex> not_real_lock(g_mutex); + sleep(1); // no-warning + + std::lock_guard<std::mutex> lock(g_mutex); + sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}} +} + +void testBlockInCriticalSectionLockGuardNested() { + testBlockInCriticalSectionLockGuard(); + sleep(1); // no-warning +} + +void testBlockInCriticalSectionUniqueLock() { + std::mutex g_mutex; + std::not_real_lock<std::mutex> not_real_lock(g_mutex); + sleep(1); // no-warning + + std::unique_lock<std::mutex> lock(g_mutex); + sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}} +} + +void testBlockInCriticalSectionUniqueLockNested() { + testBlockInCriticalSectionUniqueLock(); + sleep(1); // no-warning +} diff --git a/test/Analysis/block-in-critical-section.m b/test/Analysis/block-in-critical-section.m new file mode 100644 index 0000000000..73d58479f4 --- /dev/null +++ b/test/Analysis/block-in-critical-section.m @@ -0,0 +1,10 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.BlockInCriticalSection -verify -Wno-objc-root-class %s +// expected-no-diagnostics + +@interface SomeClass +-(void)someMethod; +@end + +void shouldNotCrash(SomeClass *o) { + [o someMethod]; +} diff --git a/test/Analysis/bstring.cpp b/test/Analysis/bstring.cpp index a6d7b40162..fea76cc082 100644 --- a/test/Analysis/bstring.cpp +++ b/test/Analysis/bstring.cpp @@ -1,8 +1,35 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s +// RUN: %clang_analyze_cc1 -DUSE_BUILTINS -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s +// RUN: %clang_analyze_cc1 -DVARIANT -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s +// RUN: %clang_analyze_cc1 -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s #include "Inputs/system-header-simulator-cxx.h" #include "Inputs/system-header-simulator-for-malloc.h" +// This provides us with four possible mempcpy() definitions. +// See also comments in bstring.c. + +#ifdef USE_BUILTINS +#define BUILTIN(f) __builtin_##f +#else /* USE_BUILTINS */ +#define BUILTIN(f) f +#endif /* USE_BUILTINS */ + +#ifdef VARIANT + +#define __mempcpy_chk BUILTIN(__mempcpy_chk) +void *__mempcpy_chk(void *__restrict__ s1, const void *__restrict__ s2, + size_t n, size_t destlen); + +#define mempcpy(a,b,c) __mempcpy_chk(a,b,c,(size_t)-1) + +#else /* VARIANT */ + +#define mempcpy BUILTIN(mempcpy) +void *mempcpy(void *__restrict__ s1, const void *__restrict__ s2, size_t n); + +#endif /* VARIANT */ + void clang_analyzer_eval(int); int *testStdCopyInvalidatesBuffer(std::vector<int> v) { @@ -36,3 +63,17 @@ int *testStdCopyBackwardInvalidatesBuffer(std::vector<int> v) { return buf; } + +namespace pr34460 { +short a; +class b { + int c; + long g; + void d() { + int e = c; + f += e; + mempcpy(f, &a, g); + } + unsigned *f; +}; +} diff --git a/test/Analysis/bug_hash_test.cpp b/test/Analysis/bug_hash_test.cpp index 0efcb5f7cd..f397d181e6 100644 --- a/test/Analysis/bug_hash_test.cpp +++ b/test/Analysis/bug_hash_test.cpp @@ -1,1345 +1,122 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.DumpBugHash -analyzer-output=plist %s -o %t.plist -// RUN: FileCheck --input-file=%t.plist %s +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,debug.ExprInspection %s -verify -int function(int p) { - return 5; +constexpr int clang_analyzer_hashDump(int) { return 5; } + +void function(int) { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void function(int)$27$clang_analyzer_hashDump(5);$Category}} } namespace { -int variadicParam(int p, ...) { - return 5; -} +void variadicParam(int, ...) { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void (anonymous namespace)::variadicParam(int, ...)$27$clang_analyzer_hashDump(5);$Category}} } +} // namespace -constexpr int f() { return 5; } +constexpr int f() { + return clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$int f()$34$returnclang_analyzer_hashDump(5);$Category}} +} namespace AA { - class X { - int priv; - X() : priv(5) { priv = 0; } +class X { + X() { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$AA::X::X()$29$clang_analyzer_hashDump(5);$Category}} + } - static int static_method() { - return 5; - } + static void static_method() { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void AA::X::static_method()$29$clang_analyzer_hashDump(5);$Category}} + variadicParam(5); + } - int method() && { - class Y { - inline int method() const & { - return 5; - } - }; + void method() && { + struct Y { + inline void method() const & { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void AA::X::method()::Y::method() const &$33$clang_analyzer_hashDump(5);$Category}} + } + }; - return 5; - } + Y y; + y.method(); - int OutOfLine(); + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void AA::X::method() &&$29$clang_analyzer_hashDump(5);$Category}} + } - X& operator=(int a) { - return *this; - } + void OutOfLine(); - operator int() { - return 0; - } + X &operator=(int) { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$class AA::X & AA::X::operator=(int)$29$clang_analyzer_hashDump(5);$Category}} + return *this; + } - explicit operator float() { - return 0; - } - }; -} + operator int() { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$AA::X::operator int()$29$clang_analyzer_hashDump(5);$Category}} + return 0; + } -int AA::X::OutOfLine() { - return 5; + explicit operator float() { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$AA::X::operator float()$29$clang_analyzer_hashDump(5);$Category}} + return 0; + } +}; +} // namespace AA + +void AA::X::OutOfLine() { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void AA::X::OutOfLine()$27$clang_analyzer_hashDump(5);$Category}} } void testLambda() { - [] () { - return; + []() { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void testLambda()::(anonymous class)::operator()() const$29$clang_analyzer_hashDump(5);$Category}} }(); } -// CHECK: <key>diagnostics</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>5</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>5</integer> -// CHECK-NEXT: <key>col</key><integer>8</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>5</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>5</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>5</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>5</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>5</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int function(int)$10$return5;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int function(int)$10$return5;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$int function(int)$10$return5;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>e7be204e83f8e5ad3c870ec011d5131d</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>function</string> -// CHECK-NEXT: <key>issue_context</key><string>function</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>5</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>10</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>10</integer> -// CHECK-NEXT: <key>col</key><integer>8</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>10</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>10</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>10</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>10</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>10</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int (anonymous namespace)::variadicParam(int, ...)$10$return5;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int (anonymous namespace)::variadicParam(int, ...)$10$return5;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$int (anonymous namespace)::variadicParam(int, ...)$10$return5;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>bc5dc0507ee90f1d14259057d25fb2b9</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>function</string> -// CHECK-NEXT: <key>issue_context</key><string>variadicParam</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>10</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>14</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>14</integer> -// CHECK-NEXT: <key>col</key><integer>26</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>14</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>14</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>14</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>14</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>14</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int f()$28$constexprintf(){return5;}$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int f()$28$constexprintf(){return5;}$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$int f()$28$constexprintf(){return5;}$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>f5471f52854dc14167fe96db50c4ba5f</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>function</string> -// CHECK-NEXT: <key>issue_context</key><string>f</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>0</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>14</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::X()$16$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::X()$16$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$AA::X::X()$16$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>d23266517ac17d5ec5e2fbbdb1922af1</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>0</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>24</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>24</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::X()$21$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::X()$21$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$AA::X::X()$21$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>7bfcc45512a6a3f61dda6e3ecebc7384</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>0</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>24</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>24</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>26</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>26</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>26</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::X()$21$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::X()$21$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$AA::X::X()$21$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>95dbfbcdd1dd6401d262994c45d088be</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>0</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>26</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>24</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>24</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::X()$28$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::X()$28$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$AA::X::X()$28$X():priv(5){priv=0;}$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>064a01551caa8eca3202f1fd55b9c692</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>0</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>28</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> -// CHECK-NEXT: <key>col</key><integer>12</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int AA::X::static_method()$14$return5;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int AA::X::static_method()$14$return5;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$int AA::X::static_method()$14$return5;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>651fcca72f8ad65771702903ecd5f68a</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>C++ method</string> -// CHECK-NEXT: <key>issue_context</key><string>static_method</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>22</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>32</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>32</integer> -// CHECK-NEXT: <key>col</key><integer>12</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>32</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>32</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>32</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>32</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>32</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int AA::X::method() &&$14$return5;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int AA::X::method() &&$14$return5;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$int AA::X::method() &&$14$return5;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>c8ac8f24467234bea1f34adf5ad5007b</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>C++ method</string> -// CHECK-NEXT: <key>issue_context</key><string>method</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>7</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>32</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>38</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>38</integer> -// CHECK-NEXT: <key>col</key><integer>12</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>38</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>38</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>38</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>38</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>38</integer> -// CHECK-NEXT: <key>col</key><integer>18</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$class AA::X & AA::X::operator=(int)$14$return*this;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$class AA::X & AA::X::operator=(int)$14$return*this;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$class AA::X & AA::X::operator=(int)$14$return*this;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>b47cf7973c9b459d9c99c483e722db8e</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>C++ method</string> -// CHECK-NEXT: <key>issue_context</key><string>operator=</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>38</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>42</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>42</integer> -// CHECK-NEXT: <key>col</key><integer>12</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>42</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>42</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>42</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>42</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>42</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::operator int()$14$return0;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::operator int()$14$return0;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$AA::X::operator int()$14$return0;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>0cbb0e1e5b03ba5b4f7f8f17504de671</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>42</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>46</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>46</integer> -// CHECK-NEXT: <key>col</key><integer>12</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>46</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>46</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>46</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>46</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>46</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::operator float()$14$return0;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$AA::X::operator float()$14$return0;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$AA::X::operator float()$14$return0;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>df306826bf89e50c1b55e1d379a761b3</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>46</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>52</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>52</integer> -// CHECK-NEXT: <key>col</key><integer>8</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>52</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>52</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>52</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>52</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>52</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int AA::X::OutOfLine()$10$return5;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$int AA::X::OutOfLine()$10$return5;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$int AA::X::OutOfLine()$10$return5;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>9dd7b17a6f62ed8c95b37a38cf71f3a9</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>C++ method</string> -// CHECK-NEXT: <key>issue_context</key><string>OutOfLine</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>52</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>56</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>56</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>58</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testLambda()$3$[](){$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testLambda()$3$[](){$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$void testLambda()$3$[](){$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>6ad4400e40885a78a0f57f585421a515</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>function</string> -// CHECK-NEXT: <key>issue_context</key><string>testLambda</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>56</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>56</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>56</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>58</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testLambda()$3$[](){$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testLambda()$3$[](){$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$void testLambda()$3$[](){$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>6ad4400e40885a78a0f57f585421a515</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>function</string> -// CHECK-NEXT: <key>issue_context</key><string>testLambda</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>56</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>56</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>56</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>58</integer> -// CHECK-NEXT: <key>col</key><integer>4</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>58</integer> -// CHECK-NEXT: <key>col</key><integer>4</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>58</integer> -// CHECK-NEXT: <key>col</key><integer>4</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>58</integer> -// CHECK-NEXT: <key>col</key><integer>4</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>58</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testLambda()$4$}();$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testLambda()$4$}();$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$void testLambda()$4$}();$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>378e6de75fb41b05bcef3950ad5ffa5e</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>function</string> -// CHECK-NEXT: <key>issue_context</key><string>testLambda</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>3</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>58</integer> -// CHECK-NEXT: <key>col</key><integer>4</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> +template <typename T> +void f(T) { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void f(T)$27$clang_analyzer_hashDump(5);$Category}} +} + +template <typename T> +struct TX { + void f(T) { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void TX::f(T)$29$clang_analyzer_hashDump(5);$Category}} + } +}; + +template <> +void f<long>(long) { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void f(long)$27$clang_analyzer_hashDump(5);$Category}} +} + +template <> +struct TX<long> { + void f(long) { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void TX<long>::f(long)$29$clang_analyzer_hashDump(5);$Category}} + } +}; + +template <typename T> +struct TTX { + template<typename S> + void f(T, S) { + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$void TTX::f(T, S)$29$clang_analyzer_hashDump(5);$Category}} + } +}; + +void g() { + // TX<int> and TX<double> is instantiated from the same code with the same + // source locations. The same error happining in both of the instantiations + // should share the common hash. This means we should not include the + // template argument for these types in the function signature. + // Note that, we still want the hash to be different for explicit + // specializations. + TX<int> x; + TX<double> y; + TX<long> xl; + x.f(1); + xl.f(1); + f(5); + f(3.0); + y.f(2); + TTX<int> z; + z.f<int>(5, 5); + f(5l); +} diff --git a/test/Analysis/bug_hash_test.m b/test/Analysis/bug_hash_test.m index 1e99d3c0b9..fbb70e5d62 100644 --- a/test/Analysis/bug_hash_test.m +++ b/test/Analysis/bug_hash_test.m @@ -1,5 +1,6 @@ -// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,debug.DumpBugHash -analyzer-output=plist %s -o %t.plist -// RUN: FileCheck --input-file=%t.plist %s +// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,debug.ExprInspection %s -verify + +void clang_analyzer_hashDump(int); @protocol NSObject + (id)alloc; @@ -15,1178 +16,21 @@ __attribute__((objc_root_class)) @end @implementation NSObject ++ (id)alloc { + return 0; +} +- (id)init { + return self; +} - (void)method:(int)arg param:(int)arg2 { - arg = 5; - return; + clang_analyzer_hashDump(5); // expected-warning {{debug.ExprInspection$NSObject::method:param:$27$clang_analyzer_hashDump(5);$Category}} } @end void testBlocks() { int x = 5; - ^{ int y = 1 + x; }(); + ^{ + clang_analyzer_hashDump(x); // expected-warning {{debug.ExprInspection$$29$clang_analyzer_hashDump(x);$Category}} + }(); } - -// CHECK: <key>diagnostics</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>9</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$NSObject::method:param:$3$arg=5;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$NSObject::method:param:$3$arg=5;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$NSObject::method:param:$3$arg=5;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>f9f569e94382c1f969aabd304581b294</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>Objective-C method</string> -// CHECK-NEXT: <key>issue_context</key><string>method:param:</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>9</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>9</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>9</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>9</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>9</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$NSObject::method:param:$9$arg=5;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$NSObject::method:param:$9$arg=5;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$NSObject::method:param:$9$arg=5;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>ca44d6aa882ee55f76e11a80d5a66372</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>Objective-C method</string> -// CHECK-NEXT: <key>issue_context</key><string>method:param:</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>19</integer> -// CHECK-NEXT: <key>col</key><integer>9</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>7</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testBlocks()$3$intx=5;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testBlocks()$3$intx=5;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$void testBlocks()$3$intx=5;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>84ec7c854c1c7849abfa03f7f20b4f06</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>function</string> -// CHECK-NEXT: <key>issue_context</key><string>testBlocks</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>11</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>11</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>11</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>11</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>11</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testBlocks()$11$intx=5;$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testBlocks()$11$intx=5;$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$void testBlocks()$11$intx=5;$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>f91db2d7b129ed60e7c9caf6f8a84d5c</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>function</string> -// CHECK-NEXT: <key>issue_context</key><string>testBlocks</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>11</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>21</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testBlocks()$3$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testBlocks()$3$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$void testBlocks()$3$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>0f1e9483a8ff59e787eaac18b68068ad</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>function</string> -// CHECK-NEXT: <key>issue_context</key><string>testBlocks</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>2</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>23</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testBlocks()$3$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$void testBlocks()$3$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$void testBlocks()$3$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>0f1e9483a8ff59e787eaac18b68068ad</string> -// CHECK-NEXT: <key>issue_context_kind</key><string>function</string> -// CHECK-NEXT: <key>issue_context</key><string>testBlocks</string> -// CHECK-NEXT: <key>issue_hash_function_offset</key><string>2</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>23</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Calling anonymous block</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Calling anonymous block</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Entered call from 'testBlocks'</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Entered call from 'testBlocks'</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>6</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>8</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>6</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>6</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>10</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$$6$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$$6$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$$6$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>8a8e42efc427e1334b77d510d3fb6361</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>6</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>23</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Calling anonymous block</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Calling anonymous block</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Entered call from 'testBlocks'</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Entered call from 'testBlocks'</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>6</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>8</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>6</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>8</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$$14$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$$14$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$$14$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>6d6028808f1d47ec5b74a417e96c2a02</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>23</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Calling anonymous block</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Calling anonymous block</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Entered call from 'testBlocks'</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Entered call from 'testBlocks'</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>14</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>18</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$$14$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$$14$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$$14$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>162138b23629276baad7dd3e8051fd6f</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>16</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>path</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>26</integer> -// CHECK-NEXT: <key>col</key><integer>5</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>23</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>0</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Calling anonymous block</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Calling anonymous block</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>Entered call from 'testBlocks'</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>Entered call from 'testBlocks'</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>3</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>6</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>8</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>control</string> -// CHECK-NEXT: <key>edges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>start</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>6</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>8</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>end</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>18</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>18</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>kind</key><string>event</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>18</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <key>ranges</key> -// CHECK-NEXT: <array> -// CHECK-NEXT: <array> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>18</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>18</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>depth</key><integer>1</integer> -// CHECK-NEXT: <key>extended_message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$$18$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>message</key> -// CHECK-NEXT: <string>debug.DumpBugHash$$18$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> -// CHECK-NEXT: <key>description</key><string>debug.DumpBugHash$$18$^{inty=1+x;}();$debug</string> -// CHECK-NEXT: <key>category</key><string>debug</string> -// CHECK-NEXT: <key>type</key><string>Dump hash components</string> -// CHECK-NEXT: <key>check_name</key><string>debug.DumpBugHash</string> -// CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>b3add78bcab0ebc3da3b640081057525</string> -// CHECK-NEXT: <key>location</key> -// CHECK-NEXT: <dict> -// CHECK-NEXT: <key>line</key><integer>27</integer> -// CHECK-NEXT: <key>col</key><integer>18</integer> -// CHECK-NEXT: <key>file</key><integer>0</integer> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </dict> -// CHECK-NEXT: </array> diff --git a/test/Analysis/builtin-functions.cpp b/test/Analysis/builtin-functions.cpp index 4e9859754d..2c19502511 100644 --- a/test/Analysis/builtin-functions.cpp +++ b/test/Analysis/builtin-functions.cpp @@ -1,6 +1,7 @@ // RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,debug.ExprInspection %s -std=c++11 -verify void clang_analyzer_eval(bool); +void clang_analyzer_warnIfReached(); void testAddressof(int x) { clang_analyzer_eval(&x == __builtin_addressof(x)); // expected-warning{{TRUE}} @@ -50,3 +51,16 @@ void test_assume_aligned_4(char *p) { q = (char*) __builtin_assume_aligned(p + 1, 16); clang_analyzer_eval(p == q); // expected-warning{{FALSE}} } + +void f(int i) { + __builtin_assume(i < 10); + clang_analyzer_eval(i < 15); // expected-warning {{TRUE}} +} + +void g(int i) { + if (i > 5) { + __builtin_assume(i < 5); + clang_analyzer_warnIfReached(); // Assumtion contradicts constraints. + // We give up the analysis on this path. + } +} diff --git a/test/Analysis/call_once.cpp b/test/Analysis/call_once.cpp new file mode 100644 index 0000000000..5013cd423e --- /dev/null +++ b/test/Analysis/call_once.cpp @@ -0,0 +1,305 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -analyzer-checker=core,debug.ExprInspection -DEMULATE_LIBSTDCPP -verify %s + +void clang_analyzer_eval(bool); + +// Faking std::std::call_once implementation. +namespace std { + +#ifndef EMULATE_LIBSTDCPP +typedef struct once_flag_s { + unsigned long __state_ = 0; +} once_flag; +#else +typedef struct once_flag_s { + int _M_once = 0; +} once_flag; +#endif + +template <class Callable, class... Args> +void call_once(once_flag &o, Callable&& func, Args&&... args) {}; + +} // namespace std + +// Check with Lambdas. +void test_called_warning() { + std::once_flag g_initialize; + int z; + + std::call_once(g_initialize, [&] { + int *x = nullptr; + int y = *x; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}} + z = 200; + }); +} + +void test_called_on_path_inside_no_warning() { + std::once_flag g_initialize; + + int *x = nullptr; + int y = 100; + int z; + + std::call_once(g_initialize, [&] { + z = 200; + x = &z; + }); + + *x = 100; // no-warning + clang_analyzer_eval(z == 100); // expected-warning{{TRUE}} +} + +void test_called_on_path_no_warning() { + std::once_flag g_initialize; + + int *x = nullptr; + int y = 100; + + std::call_once(g_initialize, [&] { + x = &y; + }); + + *x = 100; // no-warning +} + +void test_called_on_path_warning() { + std::once_flag g_initialize; + + int y = 100; + int *x = &y; + + std::call_once(g_initialize, [&] { + x = nullptr; + }); + + *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}} +} + +void test_called_once_warning() { + std::once_flag g_initialize; + + int *x = nullptr; + int y = 100; + + std::call_once(g_initialize, [&] { + x = nullptr; + }); + + std::call_once(g_initialize, [&] { + x = &y; + }); + + *x = 100; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}} +} + +void test_called_once_no_warning() { + std::once_flag g_initialize; + + int *x = nullptr; + int y = 100; + + std::call_once(g_initialize, [&] { + x = &y; + }); + + std::call_once(g_initialize, [&] { + x = nullptr; + }); + + *x = 100; // no-warning +} + +static int global = 0; +void funcPointer() { + global = 1; +} + +void test_func_pointers() { + static std::once_flag flag; + std::call_once(flag, &funcPointer); + clang_analyzer_eval(global == 1); // expected-warning{{TRUE}} +} + +template <class _Fp> +class function; // undefined +template <class _Rp, class... _ArgTypes> +struct function<_Rp(_ArgTypes...)> { + _Rp operator()(_ArgTypes...) const {}; + template <class _Fp> + function(_Fp) {}; +}; + +// Note: currently we do not support calls to std::function, +// but the analyzer should not crash either. +void test_function_objects_warning() { + int x = 0; + int *y = &x; + + std::once_flag flag; + + function<void()> func = [&]() { + y = nullptr; + }; + + std::call_once(flag, func); + + func(); + int z = *y; +} + +void test_param_passing_lambda() { + std::once_flag flag; + int x = 120; + int y = 0; + + std::call_once(flag, [&](int p) { + y = p; + }, + x); + + clang_analyzer_eval(y == 120); // expected-warning{{TRUE}} +} + +void test_param_passing_lambda_false() { + std::once_flag flag; + int x = 120; + + std::call_once(flag, [&](int p) { + x = 0; + }, + x); + + clang_analyzer_eval(x == 120); // expected-warning{{FALSE}} +} + +void test_param_passing_stored_lambda() { + std::once_flag flag; + int x = 120; + int y = 0; + + auto lambda = [&](int p) { + y = p; + }; + + std::call_once(flag, lambda, x); + clang_analyzer_eval(y == 120); // expected-warning{{TRUE}} +} + +void test_multiparam_passing_lambda() { + std::once_flag flag; + int x = 120; + + std::call_once(flag, [&](int a, int b, int c) { + x = a + b + c; + }, + 1, 2, 3); + + clang_analyzer_eval(x == 120); // expected-warning{{FALSE}} + clang_analyzer_eval(x == 6); // expected-warning{{TRUE}} +} + +static int global2 = 0; +void test_param_passing_lambda_global() { + std::once_flag flag; + global2 = 0; + std::call_once(flag, [&](int a, int b, int c) { + global2 = a + b + c; + }, + 1, 2, 3); + clang_analyzer_eval(global2 == 6); // expected-warning{{TRUE}} +} + +static int global3 = 0; +void funcptr(int a, int b, int c) { + global3 = a + b + c; +} + +void test_param_passing_funcptr() { + std::once_flag flag; + global3 = 0; + + std::call_once(flag, &funcptr, 1, 2, 3); + + clang_analyzer_eval(global3 == 6); // expected-warning{{TRUE}} +} + +void test_blocks() { + global3 = 0; + std::once_flag flag; + std::call_once(flag, ^{ + global3 = 120; + }); + clang_analyzer_eval(global3 == 120); // expected-warning{{TRUE}} +} + +int call_once() { + return 5; +} + +void test_non_std_call_once() { + int x = call_once(); + clang_analyzer_eval(x == 5); // expected-warning{{TRUE}} +} + +namespace std { +template <typename d, typename e> +void call_once(d, e); +} +void g(); +void test_no_segfault_on_different_impl() { + std::call_once(g, false); // no-warning +} + +void test_lambda_refcapture() { + static std::once_flag flag; + int a = 6; + std::call_once(flag, [&](int &a) { a = 42; }, a); + clang_analyzer_eval(a == 42); // expected-warning{{TRUE}} +} + +void test_lambda_refcapture2() { + static std::once_flag flag; + int a = 6; + std::call_once(flag, [=](int &a) { a = 42; }, a); + clang_analyzer_eval(a == 42); // expected-warning{{TRUE}} +} + +void test_lambda_fail_refcapture() { + static std::once_flag flag; + int a = 6; + std::call_once(flag, [=](int a) { a = 42; }, a); + clang_analyzer_eval(a == 42); // expected-warning{{FALSE}} +} + +void mutator(int ¶m) { + param = 42; +} +void test_reftypes_funcptr() { + static std::once_flag flag; + int a = 6; + std::call_once(flag, &mutator, a); + clang_analyzer_eval(a == 42); // expected-warning{{TRUE}} +} + +void fail_mutator(int param) { + param = 42; +} +void test_mutator_noref() { + static std::once_flag flag; + int a = 6; + std::call_once(flag, &fail_mutator, a); + clang_analyzer_eval(a == 42); // expected-warning{{FALSE}} +} + +// Function is implicitly treated as a function pointer +// even when an ampersand is not explicitly set. +void callbackn(int ¶m) { + param = 42; +} +void test_implicit_funcptr() { + int x = 0; + static std::once_flag flagn; + + std::call_once(flagn, callbackn, x); + clang_analyzer_eval(x == 42); // expected-warning{{TRUE}} +} diff --git a/test/Analysis/casts.c b/test/Analysis/casts.c index 92082fec2b..24bba8a30a 100644 --- a/test/Analysis/casts.c +++ b/test/Analysis/casts.c @@ -118,3 +118,34 @@ void castsToBool() { extern float globalFloat; clang_analyzer_eval(globalFloat); // expected-warning{{UNKNOWN}} } + +void locAsIntegerCasts(void *p) { + int x = (int) p; + clang_analyzer_eval(++x < 10); // no-crash // expected-warning{{UNKNOWN}} +} + +void multiDimensionalArrayPointerCasts() { + static int x[10][10]; + int *y1 = &(x[3][5]); + char *z = ((char *) y1) + 2; + int *y2 = (int *)(z - 2); + int *y3 = ((int *)x) + 35; // This is offset for [3][5]. + + clang_analyzer_eval(y1 == y2); // expected-warning{{TRUE}} + + // FIXME: should be FALSE (i.e. equal pointers). + clang_analyzer_eval(y1 - y2); // expected-warning{{UNKNOWN}} + // FIXME: should be TRUE (i.e. same symbol). + clang_analyzer_eval(*y1 == *y2); // expected-warning{{UNKNOWN}} + + clang_analyzer_eval(*((char *)y1) == *((char *) y2)); // expected-warning{{TRUE}} + + clang_analyzer_eval(y1 == y3); // expected-warning{{TRUE}} + + // FIXME: should be FALSE (i.e. equal pointers). + clang_analyzer_eval(y1 - y3); // expected-warning{{UNKNOWN}} + // FIXME: should be TRUE (i.e. same symbol). + clang_analyzer_eval(*y1 == *y3); // expected-warning{{UNKNOWN}} + + clang_analyzer_eval(*((char *)y1) == *((char *) y3)); // expected-warning{{TRUE}} +} diff --git a/test/Analysis/compound-literals.c b/test/Analysis/compound-literals.c new file mode 100644 index 0000000000..a2556d2a79 --- /dev/null +++ b/test/Analysis/compound-literals.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple=i386-apple-darwin10 -analyze -analyzer-checker=debug.ExprInspection -verify %s +void clang_analyzer_eval(int); + +// pr28449: Used to crash. +void foo(void) { + static const unsigned short array[] = (const unsigned short[]){0x0F00}; + // FIXME: Should be true. + clang_analyzer_eval(array[0] == 0x0F00); // expected-warning{{UNKNOWN}} +} diff --git a/test/Analysis/conversion.c b/test/Analysis/conversion.c index 5827794af2..7adb336eb2 100644 --- a/test/Analysis/conversion.c +++ b/test/Analysis/conversion.c @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -Wno-conversion -analyzer-checker=core,alpha.core.Conversion -verify %s +// RUN: %clang_analyze_cc1 -Wno-conversion -Wno-tautological-constant-compare -analyzer-checker=core,alpha.core.Conversion -verify %s unsigned char U8; signed char S8; @@ -9,9 +9,67 @@ void assign(unsigned U, signed S) { if (U > 300) S8 = U; // expected-warning {{Loss of precision in implicit conversion}} if (S > 10) - U8 = S; + U8 = S; // no-warning if (U < 200) - S8 = U; + S8 = U; // no-warning +} + +void addAssign() { + unsigned long L = 1000; + int I = -100; + U8 += L; // expected-warning {{Loss of precision in implicit conversion}} + L += I; // no-warning +} + +void subAssign() { + unsigned long L = 1000; + int I = -100; + U8 -= L; // expected-warning {{Loss of precision in implicit conversion}} + L -= I; // no-warning +} + +void mulAssign() { + unsigned long L = 1000; + int I = -1; + U8 *= L; // expected-warning {{Loss of precision in implicit conversion}} + L *= I; // expected-warning {{Loss of sign in implicit conversion}} + I = 10; + L *= I; // no-warning +} + +void divAssign() { + unsigned long L = 1000; + int I = -1; + U8 /= L; // no-warning + L /= I; // expected-warning {{Loss of sign in implicit conversion}} +} + +void remAssign() { + unsigned long L = 1000; + int I = -1; + U8 %= L; // no-warning + L %= I; // expected-warning {{Loss of sign in implicit conversion}} +} + +void andAssign() { + unsigned long L = 1000; + int I = -1; + U8 &= L; // no-warning + L &= I; // expected-warning {{Loss of sign in implicit conversion}} +} + +void orAssign() { + unsigned long L = 1000; + int I = -1; + U8 |= L; // expected-warning {{Loss of precision in implicit conversion}} + L |= I; // expected-warning {{Loss of sign in implicit conversion}} +} + +void xorAssign() { + unsigned long L = 1000; + int I = -1; + U8 ^= L; // expected-warning {{Loss of precision in implicit conversion}} + L ^= I; // expected-warning {{Loss of sign in implicit conversion}} } void init1() { @@ -21,7 +79,7 @@ void init1() { void relational(unsigned U, signed S) { if (S > 10) { - if (U < S) { + if (U < S) { // no-warning } } if (S < -10) { @@ -32,14 +90,14 @@ void relational(unsigned U, signed S) { void multiplication(unsigned U, signed S) { if (S > 5) - S = U * S; + S = U * S; // no-warning if (S < -10) S = U * S; // expected-warning {{Loss of sign}} } void division(unsigned U, signed S) { if (S > 5) - S = U / S; + S = U / S; // no-warning if (S < -10) S = U / S; // expected-warning {{Loss of sign}} } diff --git a/test/Analysis/copypaste/asm.cpp b/test/Analysis/copypaste/asm.cpp index 2e3613dfca..1d93469aa3 100644 --- a/test/Analysis/copypaste/asm.cpp +++ b/test/Analysis/copypaste/asm.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -triple x86_64-unknown-linux -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/attributes.cpp b/test/Analysis/copypaste/attributes.cpp index 083be74436..f71545130c 100644 --- a/test/Analysis/copypaste/attributes.cpp +++ b/test/Analysis/copypaste/attributes.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/autogenerated_automoc.cpp b/test/Analysis/copypaste/autogenerated_automoc.cpp new file mode 100644 index 0000000000..0f7f9c9e45 --- /dev/null +++ b/test/Analysis/copypaste/autogenerated_automoc.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -analyzer-config alpha.clone.CloneChecker:IgnoredFilesPattern="moc_|.*_automoc.cpp" -verify %s + +// Because files that have `_automoc.' in their names are most likely autogenerated, +// we suppress copy-paste warnings here. + +// expected-no-diagnostics + +void f1() { + int *p1 = new int[1]; + int *p2 = new int[1]; + if (p1) { + delete [] p1; + p1 = nullptr; + } + if (p2) { + delete [] p1; // no-warning + p2 = nullptr; + } +} diff --git a/test/Analysis/copypaste/blocks.cpp b/test/Analysis/copypaste/blocks.cpp index 10467b7248..23821560ea 100644 --- a/test/Analysis/copypaste/blocks.cpp +++ b/test/Analysis/copypaste/blocks.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -fblocks -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -fblocks -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // This tests if we search for clones in blocks. diff --git a/test/Analysis/copypaste/call.cpp b/test/Analysis/copypaste/call.cpp index 046229a30a..c5ddae5a65 100644 --- a/test/Analysis/copypaste/call.cpp +++ b/test/Analysis/copypaste/call.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/catch.cpp b/test/Analysis/copypaste/catch.cpp index cf3e8076bd..edcf44ae6f 100644 --- a/test/Analysis/copypaste/catch.cpp +++ b/test/Analysis/copypaste/catch.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -fcxx-exceptions -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -fcxx-exceptions -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/dbus_autogenerated.cpp b/test/Analysis/copypaste/dbus_autogenerated.cpp new file mode 100644 index 0000000000..1824375658 --- /dev/null +++ b/test/Analysis/copypaste/dbus_autogenerated.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:IgnoredFilesPattern="moc_|dbus_|.*_automoc" -verify %s + +// Because files that have `dbus_' in their names are most likely autogenerated, +// we suppress copy-paste warnings here. + +// expected-no-diagnostics + +void f1() { + int *p1 = new int[1]; + int *p2 = new int[1]; + if (p1) { + delete [] p1; + p1 = nullptr; + } + if (p2) { + delete [] p1; // no-warning + p2 = nullptr; + } +} diff --git a/test/Analysis/copypaste/delete.cpp b/test/Analysis/copypaste/delete.cpp index 394226bedf..4edb46035a 100644 --- a/test/Analysis/copypaste/delete.cpp +++ b/test/Analysis/copypaste/delete.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/dependent-exist.cpp b/test/Analysis/copypaste/dependent-exist.cpp index 9046353d0f..28f2ceb21a 100644 --- a/test/Analysis/copypaste/dependent-exist.cpp +++ b/test/Analysis/copypaste/dependent-exist.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -fms-extensions -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -fms-extensions -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/expr-types.cpp b/test/Analysis/copypaste/expr-types.cpp index 601f0b192a..6062be306e 100644 --- a/test/Analysis/copypaste/expr-types.cpp +++ b/test/Analysis/copypaste/expr-types.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/fold.cpp b/test/Analysis/copypaste/fold.cpp index 0aed11bcf0..fadcb49fa0 100644 --- a/test/Analysis/copypaste/fold.cpp +++ b/test/Analysis/copypaste/fold.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/function-try-block.cpp b/test/Analysis/copypaste/function-try-block.cpp index d77714591b..d0fbc50f67 100644 --- a/test/Analysis/copypaste/function-try-block.cpp +++ b/test/Analysis/copypaste/function-try-block.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -fcxx-exceptions -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -fcxx-exceptions -std=c++1z -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // Tests if function try blocks are correctly handled. diff --git a/test/Analysis/copypaste/functions.cpp b/test/Analysis/copypaste/functions.cpp index d2c607b217..2bfe591a50 100644 --- a/test/Analysis/copypaste/functions.cpp +++ b/test/Analysis/copypaste/functions.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // This tests if we search for clones in functions. diff --git a/test/Analysis/copypaste/generic.c b/test/Analysis/copypaste/generic.c index d4d4564736..2fa6c302da 100644 --- a/test/Analysis/copypaste/generic.c +++ b/test/Analysis/copypaste/generic.c @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=c11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/labels.cpp b/test/Analysis/copypaste/labels.cpp index eff3330a0f..18c5b22d3b 100644 --- a/test/Analysis/copypaste/labels.cpp +++ b/test/Analysis/copypaste/labels.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=gnu++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=gnu++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/lambda.cpp b/test/Analysis/copypaste/lambda.cpp index 17c8748943..456d83a995 100644 --- a/test/Analysis/copypaste/lambda.cpp +++ b/test/Analysis/copypaste/lambda.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // expected-no-diagnostics diff --git a/test/Analysis/copypaste/macros.cpp b/test/Analysis/copypaste/macros.cpp index bdacd4839a..ea05bbd428 100644 --- a/test/Analysis/copypaste/macros.cpp +++ b/test/Analysis/copypaste/macros.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // Tests that macros and non-macro clones aren't mixed into the same hash // group. This is currently necessary as all clones in a hash group need diff --git a/test/Analysis/copypaste/moc_autogenerated.cpp b/test/Analysis/copypaste/moc_autogenerated.cpp new file mode 100644 index 0000000000..626fe2a3dd --- /dev/null +++ b/test/Analysis/copypaste/moc_autogenerated.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:IgnoredFilesPattern="moc_|.*_automoc" -verify %s + +// Because files that have `moc_' in their names are most likely autogenerated, +// we suppress copy-paste warnings here. + +// expected-no-diagnostics + +void f1() { + int *p1 = new int[1]; + int *p2 = new int[1]; + if (p1) { + delete [] p1; + p1 = nullptr; + } + if (p2) { + delete [] p1; // no-warning + p2 = nullptr; + } +} diff --git a/test/Analysis/copypaste/not-autogenerated.cpp b/test/Analysis/copypaste/not-autogenerated.cpp new file mode 100644 index 0000000000..d54ee176fa --- /dev/null +++ b/test/Analysis/copypaste/not-autogenerated.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -analyzer-config alpha.clone.CloneChecker:IgnoredFilesPattern="moc_|ui_|dbus_|.*_automoc" -verify %s + +void f1() { + int *p1 = new int[1]; + int *p2 = new int[1]; + if (p1) { + delete [] p1; // expected-note{{Similar code using 'p1' here}} + p1 = nullptr; + } + if (p2) { + delete [] p1; // expected-warning{{Potential copy-paste error; did you really mean to use 'p1' here?}} + p2 = nullptr; + } +} diff --git a/test/Analysis/copypaste/objc-methods.m b/test/Analysis/copypaste/objc-methods.m index e63c7f6288..0a84370793 100644 --- a/test/Analysis/copypaste/objc-methods.m +++ b/test/Analysis/copypaste/objc-methods.m @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -Wno-objc-root-class -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -Wno-objc-root-class -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // This tests if we search for clones in Objective-C methods. diff --git a/test/Analysis/copypaste/plist-diagnostics-notes-as-events.cpp b/test/Analysis/copypaste/plist-diagnostics-notes-as-events.cpp index 7c4f35525f..d4402a9dd5 100644 --- a/test/Analysis/copypaste/plist-diagnostics-notes-as-events.cpp +++ b/test/Analysis/copypaste/plist-diagnostics-notes-as-events.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-output=plist -analyzer-config notes-as-events=true -o %t.plist -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -analyzer-output=plist -analyzer-config notes-as-events=true -o %t.plist -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // RUN: FileCheck --input-file=%t.plist %s void log(); diff --git a/test/Analysis/copypaste/plist-diagnostics.cpp b/test/Analysis/copypaste/plist-diagnostics.cpp index e2fa7597ca..c138c6f1f9 100644 --- a/test/Analysis/copypaste/plist-diagnostics.cpp +++ b/test/Analysis/copypaste/plist-diagnostics.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-output=plist -o %t.plist -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -analyzer-output=plist -o %t.plist -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // RUN: FileCheck --input-file=%t.plist %s void log(); diff --git a/test/Analysis/copypaste/sub-sequences.cpp b/test/Analysis/copypaste/sub-sequences.cpp index 798662d256..d2c08350ab 100644 --- a/test/Analysis/copypaste/sub-sequences.cpp +++ b/test/Analysis/copypaste/sub-sequences.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // This tests if sub-sequences can match with normal sequences. diff --git a/test/Analysis/copypaste/suspicious-clones.cpp b/test/Analysis/copypaste/suspicious-clones.cpp index 3a760e2562..ae29b0e16d 100644 --- a/test/Analysis/copypaste/suspicious-clones.cpp +++ b/test/Analysis/copypaste/suspicious-clones.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:ReportSuspiciousClones=true -analyzer-config alpha.clone.CloneChecker:ReportNormalClones=false -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:ReportSuspiciousClones=true -analyzer-config alpha.clone.CloneChecker:ReportNormalClones=false -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s // Tests finding a suspicious clone that references local variables. diff --git a/test/Analysis/copypaste/text-diagnostics.cpp b/test/Analysis/copypaste/text-diagnostics.cpp index a6e358cc85..a3132e9534 100644 --- a/test/Analysis/copypaste/text-diagnostics.cpp +++ b/test/Analysis/copypaste/text-diagnostics.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_analyze_cc1 -analyzer-output=text -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -verify %s +// RUN: %clang_analyze_cc1 -analyzer-output=text -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:MinimumCloneComplexity=10 -verify %s void log(); diff --git a/test/Analysis/copypaste/ui_autogenerated.cpp b/test/Analysis/copypaste/ui_autogenerated.cpp new file mode 100644 index 0000000000..a08c33fe9e --- /dev/null +++ b/test/Analysis/copypaste/ui_autogenerated.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=alpha.clone.CloneChecker -analyzer-config alpha.clone.CloneChecker:IgnoredFilesPattern="moc_|ui_|.*_automoc" -verify %s + +// Because files that have `ui_' in their names are most likely autogenerated, +// we suppress copy-paste warnings here. + +// expected-no-diagnostics + +void f1() { + int *p1 = new int[1]; + int *p2 = new int[1]; + if (p1) { + delete [] p1; + p1 = nullptr; + } + if (p2) { + delete [] p1; // no-warning + p2 = nullptr; + } +} diff --git a/test/Analysis/ctor.mm b/test/Analysis/ctor.mm index 646229aac9..e903263431 100644 --- a/test/Analysis/ctor.mm +++ b/test/Analysis/ctor.mm @@ -199,7 +199,7 @@ namespace PODUninitialized { Inner p; }; - void testPOD() { + void testPOD(const POD &pp) { POD p; p.x = 1; POD p2 = p; // no-warning @@ -210,6 +210,15 @@ namespace PODUninitialized { // Use rvalues as well. clang_analyzer_eval(POD(p3).x == 1); // expected-warning{{TRUE}} + // Copy from symbolic references correctly. + POD p4 = pp; + // Make sure that p4.x contains a symbol after copy. + if (p4.x > 0) + clang_analyzer_eval(p4.x > 0); // expected-warning{{TRUE}} + // FIXME: Element region gets in the way, so these aren't the same symbols + // as they should be. + clang_analyzer_eval(pp.x == p4.x); // expected-warning{{UNKNOWN}} + PODWrapper w; w.p.y = 1; PODWrapper w2 = w; // no-warning @@ -704,3 +713,20 @@ namespace PR19579 { }; } } + +namespace NoCrashOnEmptyBaseOptimization { + struct NonEmptyBase { + int X; + explicit NonEmptyBase(int X) : X(X) {} + }; + + struct EmptyBase {}; + + struct S : NonEmptyBase, EmptyBase { + S() : NonEmptyBase(0), EmptyBase() {} + }; + + void testSCtorNoCrash() { + S s; + } +} diff --git a/test/Analysis/diagnostics/diag-cross-file-boundaries.c b/test/Analysis/diagnostics/diag-cross-file-boundaries.c deleted file mode 100644 index b975af3fb2..0000000000 --- a/test/Analysis/diagnostics/diag-cross-file-boundaries.c +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-output=html -o PR12421.html %s 2>&1 | FileCheck %s - -// Test for PR12421 -#include "diag-cross-file-boundaries.h" - -int main(){ - f(); - return 0; -} - -// CHECK: warning: Path diagnostic report is not generated. diff --git a/test/Analysis/diagnostics/explicit-suppression.cpp b/test/Analysis/diagnostics/explicit-suppression.cpp index 193846c082..96d2b4a7d0 100644 --- a/test/Analysis/diagnostics/explicit-suppression.cpp +++ b/test/Analysis/diagnostics/explicit-suppression.cpp @@ -19,6 +19,6 @@ class C { void testCopyNull(C *I, C *E) { std::copy(I, E, (C *)0); #ifndef SUPPRESSED - // expected-warning@../Inputs/system-header-simulator-cxx.h:191 {{Called C++ object pointer is null}} + // expected-warning@../Inputs/system-header-simulator-cxx.h:490 {{Called C++ object pointer is null}} #endif } diff --git a/test/Analysis/diagnostics/report-issues-within-main-file.cpp b/test/Analysis/diagnostics/report-issues-within-main-file.cpp index 784fdba972..e1dccc8e32 100644 --- a/test/Analysis/diagnostics/report-issues-within-main-file.cpp +++ b/test/Analysis/diagnostics/report-issues-within-main-file.cpp @@ -945,7 +945,7 @@ void callInMacroArg() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete' (within a call to '~auto_ptr')</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Bad deallocator</string> // CHECK-NEXT: <key>check_name</key><string>unix.MismatchedDeallocator</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> diff --git a/test/Analysis/edges-new.mm b/test/Analysis/edges-new.mm index 217cd4aa46..f310f1bfa1 100644 --- a/test/Analysis/edges-new.mm +++ b/test/Analysis/edges-new.mm @@ -20042,7 +20042,7 @@ namespace rdar14960554 { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'buf'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -20284,11 +20284,11 @@ namespace rdar14960554 { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Bad deallocator</string> // CHECK-NEXT: <key>check_name</key><string>unix.MismatchedDeallocator</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> -// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>d9dbbf68db41ab74e2158f4b131abe34</string> +// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>046c88d1c91ff46d6506dff5ff880756</string> // CHECK-NEXT: <key>issue_hash_function_offset</key><string>0</string> // CHECK-NEXT: <key>location</key> // CHECK-NEXT: <dict> diff --git a/test/Analysis/enum.cpp b/test/Analysis/enum.cpp index e26b8f00d9..96408473ce 100644 --- a/test/Analysis/enum.cpp +++ b/test/Analysis/enum.cpp @@ -24,3 +24,46 @@ void testCasting(int i) { clang_analyzer_eval(j == 0); // expected-warning{{FALSE}} } } + +enum class EnumBool : bool { + F = false, + T = true +}; + +bool testNoCrashOnSwitchEnumBool(EnumBool E) { + switch (E) { + case EnumBool::F: + return false; + } + return true; +} + +bool testNoCrashOnSwitchEnumBoolConstant() { + EnumBool E = EnumBool::F; + switch (E) { + case EnumBool::F: + return false; + } + return true; +} + +typedef __INTPTR_TYPE__ intptr_t; +bool testNoCrashOnSwitchEnumBoolConstantCastedFromNullptr() { + EnumBool E = static_cast<EnumBool>((intptr_t)nullptr); + switch (E) { + case EnumBool::F: + return false; + } + return true; +} + +bool testNoCrashOnSwitchEnumBoolConstantCastedFromPtr() { + int X; + intptr_t P = (intptr_t)&X; + EnumBool E = static_cast<EnumBool>(P); + switch (E) { + case EnumBool::F: + return false; + } + return true; +} diff --git a/test/Analysis/exercise-ps.c b/test/Analysis/exercise-ps.c index 577b88bf83..c459260c2a 100644 --- a/test/Analysis/exercise-ps.c +++ b/test/Analysis/exercise-ps.c @@ -21,3 +21,11 @@ static void f2(void *buf) { memcpy((&x[1]), (buf), 1); // expected-warning{{implicitly declaring library function 'memcpy' with type 'void *(void *, const void *}} \ // expected-note{{include the header <string.h> or explicitly provide a declaration for 'memcpy'}} } + +// AllocaRegion is untyped. Void pointer isn't of much help either. Before +// realizing that the value is undefined, we need to somehow figure out +// what type of value do we expect. +void f3(void *dest) { + void *src = __builtin_alloca(5); + memcpy(dest, src, 1); // expected-warning{{2nd function call argument is a pointer to uninitialized value}} +} diff --git a/test/Analysis/expr-inspection.c b/test/Analysis/expr-inspection.c index 1e21efb4ef..ec0e682b65 100644 --- a/test/Analysis/expr-inspection.c +++ b/test/Analysis/expr-inspection.c @@ -8,6 +8,7 @@ void clang_analyzer_numTimesReached(); void foo(int x) { clang_analyzer_dump(x); // expected-warning{{reg_$0<int x>}} + clang_analyzer_dump(x + (-1)); // expected-warning{{(reg_$0<int x>) + -1}} int y = 1; clang_analyzer_printState(); for (; y < 3; ++y) @@ -19,4 +20,4 @@ void foo(int x) { // CHECK: Expressions: // CHECK-NEXT: clang_analyzer_printState : &code{clang_analyzer_printState} -// CHECK-NEXT: Ranges are empty. +// CHECK-NEXT: {{(Ranges are empty.)|(Constraints:[[:space:]]*$)}} diff --git a/test/Analysis/func-mapping-test.cpp b/test/Analysis/func-mapping-test.cpp new file mode 100644 index 0000000000..37e653882b --- /dev/null +++ b/test/Analysis/func-mapping-test.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_func_map %s -- | FileCheck %s + +int f(int) { + return 0; +} + +// CHECK: c:@F@f#I# diff --git a/test/Analysis/gmalloc.c b/test/Analysis/gmalloc.c index 10c4fe2840..50413e2e9b 100644 --- a/test/Analysis/gmalloc.c +++ b/test/Analysis/gmalloc.c @@ -13,6 +13,12 @@ gpointer g_realloc(gpointer mem, gsize n_bytes); gpointer g_try_malloc(gsize n_bytes); gpointer g_try_malloc0(gsize n_bytes); gpointer g_try_realloc(gpointer mem, gsize n_bytes); +gpointer g_malloc_n(gsize n_blocks, gsize n_block_bytes); +gpointer g_malloc0_n(gsize n_blocks, gsize n_block_bytes); +gpointer g_realloc_n(gpointer mem, gsize n_blocks, gsize n_block_bytes); +gpointer g_try_malloc_n(gsize n_blocks, gsize n_block_bytes); +gpointer g_try_malloc0_n(gsize n_blocks, gsize n_block_bytes); +gpointer g_try_realloc_n(gpointer mem, gsize n_blocks, gsize n_block_bytes); void g_free(gpointer mem); gpointer g_memdup(gconstpointer mem, guint byte_size); @@ -25,6 +31,12 @@ void f1() { gpointer g3 = g_try_malloc(n_bytes); gpointer g4 = g_try_malloc0(n_bytes); g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); g_free(g1); g_free(g2); @@ -38,6 +50,12 @@ void f2() { gpointer g3 = g_try_malloc(n_bytes); gpointer g4 = g_try_malloc0(n_bytes); g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); g_free(g1); g_free(g2); @@ -52,8 +70,100 @@ void f3() { gpointer g3 = g_try_malloc(n_bytes); gpointer g4 = g_try_malloc0(n_bytes); g3 = g_try_realloc(g3, n_bytes * 2); // expected-warning{{Potential leak of memory pointed to by 'g4'}} + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g6'}} + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g5'}} + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}} + + g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}} + g_free(g2); + g_free(g3); +} + +void f4() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g6'}} + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g5'}} + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}} + + g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}} + g_free(g2); + g_free(g3); + g_free(g4); +} + +void f5() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g6'}} + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}} + + g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}} + g_free(g2); + g_free(g3); + g_free(g4); + g_free(g5); +} + +void f6() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}} + + g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}} + g_free(g2); + g_free(g3); + g_free(g4); + g_free(g5); + g_free(g6); +} + +void f7() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}} g_free(g1); g_free(g2); g_free(g3); + g_free(g4); + g_free(g5); + g_free(g6); + g_free(g7); } diff --git a/test/Analysis/gtest.cpp b/test/Analysis/gtest.cpp index 5797a773b4..98f415eea4 100644 --- a/test/Analysis/gtest.cpp +++ b/test/Analysis/gtest.cpp @@ -151,3 +151,17 @@ void testConstrainState(int p) { ASSERT_TRUE(false); clang_analyzer_warnIfReached(); // no-warning } + +void testAssertSymbolicPtr(const bool *b) { + ASSERT_TRUE(*b); // no-crash + + // FIXME: Our solver doesn't handle this well yet. + clang_analyzer_eval(*b); // expected-warning{{UNKNOWN}} +} + +void testAssertSymbolicRef(const bool &b) { + ASSERT_TRUE(b); // no-crash + + // FIXME: Our solver doesn't handle this well yet. + clang_analyzer_eval(b); // expected-warning{{UNKNOWN}} +} diff --git a/test/Analysis/html-diag-singlefile.c b/test/Analysis/html-diag-singlefile.c new file mode 100644 index 0000000000..fc0dcc7a42 --- /dev/null +++ b/test/Analysis/html-diag-singlefile.c @@ -0,0 +1,14 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-output=html-single-file -o D30406.html %s 2>&1 | FileCheck %s + +// Check that single file HTML output does not process multi-file diagnostics. +// (This used to test for PR12421, before the introduction of the html-single-file format) + +#include "html-diag-singlefile.h" + +int main(){ + f(); + return 0; +} + +// CHECK: warning: Path diagnostic report is not generated. diff --git a/test/Analysis/diagnostics/diag-cross-file-boundaries.h b/test/Analysis/html-diag-singlefile.h index 1af7d1f1fb..1af7d1f1fb 100644 --- a/test/Analysis/diagnostics/diag-cross-file-boundaries.h +++ b/test/Analysis/html-diag-singlefile.h diff --git a/test/Analysis/html-diags-analyze-headers.c b/test/Analysis/html-diags-analyze-headers.c new file mode 100644 index 0000000000..fa5f21de04 --- /dev/null +++ b/test/Analysis/html-diags-analyze-headers.c @@ -0,0 +1,10 @@ +// RUN: mkdir -p %t.dir +// RUN: %clang_analyze_cc1 -analyzer-opt-analyze-headers -analyzer-output=html -analyzer-checker=core -o %t.dir %s +// RUN: ls %t.dir | grep report +// RUN: rm -rf %t.dir + +// This tests that we emit HTML diagnostics for reports in headers when the +// analyzer is run with -analyzer-opt-analyze-headers. This was handled +// incorrectly in the first iteration of D30406. + +#include "html-diags-analyze-headers.h" diff --git a/test/Analysis/html-diags-analyze-headers.h b/test/Analysis/html-diags-analyze-headers.h new file mode 100644 index 0000000000..3641ca9c04 --- /dev/null +++ b/test/Analysis/html-diags-analyze-headers.h @@ -0,0 +1,5 @@ +#include "html-diags-multifile.h" + +void test_call_macro() { + has_bug(0); +} diff --git a/test/Analysis/html-diags-multifile.c b/test/Analysis/html-diags-multifile.c index ce1f72b6bb..ff7b625ad0 100644 --- a/test/Analysis/html-diags-multifile.c +++ b/test/Analysis/html-diags-multifile.c @@ -1,10 +1,9 @@ // RUN: mkdir -p %t.dir // RUN: %clang_analyze_cc1 -analyzer-output=html -analyzer-checker=core -o %t.dir %s -// RUN: ls %t.dir | not grep report +// RUN: ls %t.dir | grep report // RUN: rm -fR %t.dir -// This tests that we do not currently emit HTML diagnostics for reports that -// cross file boundaries. +// This tests that we emit HTML diagnostics for reports that cross file boundaries. #include "html-diags-multifile.h" diff --git a/test/Analysis/html-diags.c b/test/Analysis/html-diags.c index 182bcfbdfa..89f1e8ba79 100644 --- a/test/Analysis/html-diags.c +++ b/test/Analysis/html-diags.c @@ -1,12 +1,18 @@ -// RUN: rm -fR %T/dir -// RUN: mkdir %T/dir -// RUN: %clang_analyze_cc1 -analyzer-output=html -analyzer-checker=core -o %T/dir %s -// RUN: ls %T/dir | grep report +// RUN: rm -fR %t +// RUN: mkdir %t +// RUN: %clang_analyze_cc1 -analyzer-output=html -analyzer-checker=core -o %t %s +// RUN: ls %t | grep report + +// D30406: Test new html-single-file output +// RUN: rm -fR %t +// RUN: mkdir %t +// RUN: %clang_analyze_cc1 -analyzer-output=html-single-file -analyzer-checker=core -o %t %s +// RUN: ls %t | grep report // PR16547: Test relative paths -// RUN: cd %T/dir +// RUN: cd %t // RUN: %clang_analyze_cc1 -analyzer-output=html -analyzer-checker=core -o testrelative %s -// RUN: ls %T/dir/testrelative | grep report +// RUN: ls %t/testrelative | grep report // Currently this test mainly checks that the HTML diagnostics doesn't crash // when handling macros will calls with macros. We should actually validate diff --git a/test/Analysis/initializer.cpp b/test/Analysis/initializer.cpp index c73635686d..e9658a067c 100644 --- a/test/Analysis/initializer.cpp +++ b/test/Analysis/initializer.cpp @@ -1,7 +1,9 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config c++-inlining=constructors -std=c++11 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-inlining=constructors -std=c++11 -verify %s void clang_analyzer_eval(bool); +#include "Inputs/system-header-simulator-cxx.h" + class A { int x; public: @@ -204,3 +206,17 @@ struct C { const char(&f)[2]; }; } + +namespace CXX_initializer_lists { +struct C { + C(std::initializer_list<int *> list); +}; +void foo() { + C empty{}; // no-crash + + // Do not warn that 'x' leaks. It might have been deleted by + // the destructor of 'c'. + int *x = new int; + C c{x}; // no-warning +} +} diff --git a/test/Analysis/inlining/inline-defensive-checks.c b/test/Analysis/inlining/inline-defensive-checks.c index 4029da651b..9f211b502b 100644 --- a/test/Analysis/inlining/inline-defensive-checks.c +++ b/test/Analysis/inlining/inline-defensive-checks.c @@ -1,7 +1,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-config suppress-inlined-defensive-checks=true -verify %s // Perform inline defensive checks. -void idc(int *p) { +void idc(void *p) { if (p) ; } @@ -139,3 +139,54 @@ void idcTrackZeroThroughDoubleAssignemnt(int x) { int z = y; idcTriggerZeroValueThroughCall(z); } + +struct S { + int f1; + int f2; +}; + +void idcTrackZeroValueThroughUnaryPointerOperators(struct S *s) { + idc(s); + *(&(s->f1)) = 7; // no-warning +} + +void idcTrackZeroValueThroughUnaryPointerOperatorsWithOffset1(struct S *s) { + idc(s); + int *x = &(s->f2); + *x = 7; // no-warning +} + +void idcTrackZeroValueThroughUnaryPointerOperatorsWithOffset2(struct S *s) { + idc(s); + int *x = &(s->f2) - 1; + // FIXME: Should not warn. + *x = 7; // expected-warning{{Dereference of null pointer}} +} + +void idcTrackZeroValueThroughUnaryPointerOperatorsWithAssignment(struct S *s) { + idc(s); + int *x = &(s->f1); + *x = 7; // no-warning +} + +void idcTrackZeroValueThroughManyUnaryPointerOperatorsWithAssignment(struct S *s) { + idc(s); + int *x = &*&(s->f1); + *x = 7; // no-warning +} + +void idcTrackZeroValueThroughManyUnaryPointerOperatorsWithAssignmentAndUnaryIncrement(struct S *s) { + idc(s); + int *x = &*&((++s)->f1); + *x = 7; // no-warning +} + + +struct S2 { + int a[1]; +}; + +void idcTrackZeroValueThroughUnaryPointerOperatorsWithArrayField(struct S2 *s) { + idc(s); + *(&(s->a[0])) = 7; // no-warning +} diff --git a/test/Analysis/inlining/inline-defensive-checks.cpp b/test/Analysis/inlining/inline-defensive-checks.cpp index 6a803fa695..eaae8d2ae2 100644 --- a/test/Analysis/inlining/inline-defensive-checks.cpp +++ b/test/Analysis/inlining/inline-defensive-checks.cpp @@ -70,4 +70,17 @@ int *retNull() { void test(int *p1, int *p2) { idc(p1); Foo f(p1); -}
\ No newline at end of file +} + +struct Bar { + int x; +}; +void idcBar(Bar *b) { + if (b) + ; +} +void testRefToField(Bar *b) { + idcBar(b); + int &x = b->x; // no-warning + x = 5; +} diff --git a/test/Analysis/iterator-past-end.cpp b/test/Analysis/iterator-past-end.cpp deleted file mode 100644 index 252d1044bd..0000000000 --- a/test/Analysis/iterator-past-end.cpp +++ /dev/null @@ -1,205 +0,0 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorPastEnd -analyzer-eagerly-assume -analyzer-config c++-container-inlining=false %s -verify -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorPastEnd -analyzer-eagerly-assume -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify - -#include "Inputs/system-header-simulator-cxx.h" - -void simple_good(const std::vector<int> &v) { - auto i = v.end(); - if (i != v.end()) - *i; // no-warning -} - -void simple_good_negated(const std::vector<int> &v) { - auto i = v.end(); - if (!(i == v.end())) - *i; // no-warning -} - -void simple_bad(const std::vector<int> &v) { - auto i = v.end(); - *i; // expected-warning{{Iterator accessed past its end}} -} - -void copy(const std::vector<int> &v) { - auto i1 = v.end(); - auto i2 = i1; - *i2; // expected-warning{{Iterator accessed past its end}} -} - -void decrease(const std::vector<int> &v) { - auto i = v.end(); - --i; - *i; // no-warning -} - -void copy_and_decrease1(const std::vector<int> &v) { - auto i1 = v.end(); - auto i2 = i1; - --i1; - *i1; // no-warning -} - -void copy_and_decrease2(const std::vector<int> &v) { - auto i1 = v.end(); - auto i2 = i1; - --i1; - *i2; // expected-warning{{Iterator accessed past its end}} -} - -void copy_and_increase1(const std::vector<int> &v) { - auto i1 = v.begin(); - auto i2 = i1; - ++i1; - if (i1 == v.end()) - *i2; // no-warning -} - -void copy_and_increase2(const std::vector<int> &v) { - auto i1 = v.begin(); - auto i2 = i1; - ++i1; - if (i2 == v.end()) - *i2; // expected-warning{{Iterator accessed past its end}} -} - -void good_find(std::vector<int> &vec, int e) { - auto first = std::find(vec.begin(), vec.end(), e); - if (vec.end() != first) - *first; // no-warning -} - -void bad_find(std::vector<int> &vec, int e) { - auto first = std::find(vec.begin(), vec.end(), e); - *first; // expected-warning{{Iterator accessed past its end}} -} - -void good_find_end(std::vector<int> &vec, std::vector<int> &seq) { - auto last = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end()); - if (vec.end() != last) - *last; // no-warning -} - -void bad_find_end(std::vector<int> &vec, std::vector<int> &seq) { - auto last = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end()); - *last; // expected-warning{{Iterator accessed past its end}} -} - -void good_find_first_of(std::vector<int> &vec, std::vector<int> &seq) { - auto first = - std::find_first_of(vec.begin(), vec.end(), seq.begin(), seq.end()); - if (vec.end() != first) - *first; // no-warning -} - -void bad_find_first_of(std::vector<int> &vec, std::vector<int> &seq) { - auto first = std::find_end(vec.begin(), vec.end(), seq.begin(), seq.end()); - *first; // expected-warning{{Iterator accessed past its end}} -} - -bool odd(int i) { return i % 2; } - -void good_find_if(std::vector<int> &vec) { - auto first = std::find_if(vec.begin(), vec.end(), odd); - if (vec.end() != first) - *first; // no-warning -} - -void bad_find_if(std::vector<int> &vec, int e) { - auto first = std::find_if(vec.begin(), vec.end(), odd); - *first; // expected-warning{{Iterator accessed past its end}} -} - -void good_find_if_not(std::vector<int> &vec) { - auto first = std::find_if_not(vec.begin(), vec.end(), odd); - if (vec.end() != first) - *first; // no-warning -} - -void bad_find_if_not(std::vector<int> &vec, int e) { - auto first = std::find_if_not(vec.begin(), vec.end(), odd); - *first; // expected-warning{{Iterator accessed past its end}} -} - -void good_lower_bound(std::vector<int> &vec, int e) { - auto first = std::lower_bound(vec.begin(), vec.end(), e); - if (vec.end() != first) - *first; // no-warning -} - -void bad_lower_bound(std::vector<int> &vec, int e) { - auto first = std::lower_bound(vec.begin(), vec.end(), e); - *first; // expected-warning{{Iterator accessed past its end}} -} - -void good_upper_bound(std::vector<int> &vec, int e) { - auto last = std::lower_bound(vec.begin(), vec.end(), e); - if (vec.end() != last) - *last; // no-warning -} - -void bad_upper_bound(std::vector<int> &vec, int e) { - auto last = std::lower_bound(vec.begin(), vec.end(), e); - *last; // expected-warning{{Iterator accessed past its end}} -} - -void good_search(std::vector<int> &vec, std::vector<int> &seq) { - auto first = std::search(vec.begin(), vec.end(), seq.begin(), seq.end()); - if (vec.end() != first) - *first; // no-warning -} - -void bad_search(std::vector<int> &vec, std::vector<int> &seq) { - auto first = std::search(vec.begin(), vec.end(), seq.begin(), seq.end()); - *first; // expected-warning{{Iterator accessed past its end}} -} - -void good_search_n(std::vector<int> &vec, std::vector<int> &seq) { - auto nth = std::search_n(vec.begin(), vec.end(), seq.begin(), seq.end()); - if (vec.end() != nth) - *nth; // no-warning -} - -void bad_search_n(std::vector<int> &vec, std::vector<int> &seq) { - auto nth = std::search_n(vec.begin(), vec.end(), seq.begin(), seq.end()); - *nth; // expected-warning{{Iterator accessed past its end}} -} - -template <class InputIterator, class T> -InputIterator nonStdFind(InputIterator first, InputIterator last, - const T &val) { - for (auto i = first; i != last; ++i) { - if (*i == val) { - return i; - } - } - return last; -} - -void good_non_std_find(std::vector<int> &vec, int e) { - auto first = nonStdFind(vec.begin(), vec.end(), e); - if (vec.end() != first) - *first; // no-warning -} - -void bad_non_std_find(std::vector<int> &vec, int e) { - auto first = nonStdFind(vec.begin(), vec.end(), e); - *first; // expected-warning{{Iterator accessed past its end}} -} - -void tricky(std::vector<int> &vec, int e) { - const auto first = vec.begin(); - const auto comp1 = (first != vec.end()), comp2 = (first == vec.end()); - if (comp1) - *first; -} - -void loop(std::vector<int> &vec, int e) { - auto start = vec.begin(); - while (true) { - auto item = std::find(start, vec.end(), e); - if (item == vec.end()) - break; - *item; // no-warning - start = ++item; // no-warning - } -} diff --git a/test/Analysis/iterator-range.cpp b/test/Analysis/iterator-range.cpp new file mode 100644 index 0000000000..79b45188ab --- /dev/null +++ b/test/Analysis/iterator-range.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-eagerly-assume -analyzer-config c++-container-inlining=false %s -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-eagerly-assume -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify + +#include "Inputs/system-header-simulator-cxx.h" + +void clang_analyzer_warnIfReached(); + +void simple_good_end(const std::vector<int> &v) { + auto i = v.end(); + if (i != v.end()) { + clang_analyzer_warnIfReached(); + *i; // no-warning + } +} + +void simple_bad_end(const std::vector<int> &v) { + auto i = v.end(); + *i; // expected-warning{{Iterator accessed outside of its range}} +} diff --git a/test/Analysis/lifetime-cfg-output.cpp b/test/Analysis/lifetime-cfg-output.cpp new file mode 100644 index 0000000000..1e6f56df6a --- /dev/null +++ b/test/Analysis/lifetime-cfg-output.cpp @@ -0,0 +1,783 @@ +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -analyze -analyzer-checker=debug.DumpCFG -analyzer-config cfg-lifetime=true -analyzer-config cfg-implicit-dtors=false %s > %t 2>&1 +// RUN: FileCheck --input-file=%t %s + +extern bool UV; +class A { +public: + // CHECK: [B2 (ENTRY)] + // CHECK-NEXT: Succs (1): B1 + // CHECK: [B1] + // CHECK-NEXT: 1: true + // CHECK-NEXT: 2: UV + // CHECK-NEXT: 3: [B1.2] = [B1.1] + // CHECK-NEXT: Preds (1): B2 + // CHECK-NEXT: Succs (1): B0 + // CHECK: [B0 (EXIT)] + // CHECK-NEXT: Preds (1): B1 + A() { + UV = true; + } + // CHECK: [B3 (ENTRY)] + // CHECK-NEXT: Succs (1): B2 + // CHECK: [B1] + // CHECK-NEXT: 1: 0 + // CHECK-NEXT: 2: this + // CHECK-NEXT: 3: [B1.2]->p + // CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, LValueToRValue, int *) + // CHECK-NEXT: 5: *[B1.4] + // CHECK-NEXT: 6: [B1.5] = [B1.1] + // CHECK-NEXT: Preds (1): B2 + // CHECK-NEXT: Succs (1): B0 + // CHECK: [B2] + // CHECK-NEXT: 1: this + // CHECK-NEXT: 2: [B2.1]->p + // CHECK-NEXT: 3: [B2.2] (ImplicitCastExpr, LValueToRValue, int *) + // CHECK-NEXT: 4: [B2.3] (ImplicitCastExpr, PointerToBoolean, _Bool) + // CHECK-NEXT: T: if [B2.4] + // CHECK-NEXT: Preds (1): B3 + // CHECK-NEXT: Succs (2): B1 B0 + // CHECK: [B0 (EXIT)] + // CHECK-NEXT: Preds (2): B1 B2 + ~A() { + if (p) + *p = 0; + } + // CHECK: [B2 (ENTRY)] + // CHECK-NEXT: Succs (1): B1 + // CHECK: [B1] + // CHECK-NEXT: 1: 1 + // CHECK-NEXT: 2: return [B1.1]; + // CHECK-NEXT: Preds (1): B2 + // CHECK-NEXT: Succs (1): B0 + // CHECK: [B0 (EXIT)] + // CHECK-NEXT: Preds (1): B1 + operator int() const { return 1; } + int *p; +}; + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: a +// CHECK-NEXT: 4: [B1.3] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 5: const A &b = a; +// CHECK-NEXT: 6: A() (CXXConstructExpr, class A) +// CHECK-NEXT: 7: [B1.6] (BindTemporary) +// CHECK-NEXT: 8: [B1.7] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 9: [B1.8] +// CHECK-NEXT: 10: const A &c = A(); +// CHECK-NEXT: 11: [B1.10] (Lifetime ends) +// CHECK-NEXT: 12: [B1.2] (Lifetime ends) +// CHECK-NEXT: 13: [B1.5] (Lifetime ends) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_const_ref() { + A a; + const A &b = a; + const A &c = A(); +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A [2]) +// CHECK-NEXT: 2: A a[2]; +// CHECK-NEXT: 3: (CXXConstructExpr, class A [0]) +// CHECK-NEXT: 4: A b[0]; +// lifetime of a ends when its destructors are run +// CHECK-NEXT: 5: [B1.2] (Lifetime ends) +// lifetime of b ends when its storage duration ends +// CHECK-NEXT: 6: [B1.4] (Lifetime ends) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_array() { + A a[2]; + A b[0]; +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A c; +// CHECK-NEXT: 5: (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A d; +// CHECK-NEXT: 7: [B1.6] (Lifetime ends) +// CHECK-NEXT: 8: [B1.4] (Lifetime ends) +// CHECK-NEXT: 9: (CXXConstructExpr, class A) +// CHECK-NEXT: 10: A b; +// CHECK-NEXT: 11: [B1.10] (Lifetime ends) +// CHECK-NEXT: 12: [B1.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_scope() { + A a; + { + A c; + A d; + } + A b; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B1.2] (Lifetime ends) +// CHECK-NEXT: 4: [B3.4] (Lifetime ends) +// CHECK-NEXT: 5: [B3.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B3.4] (Lifetime ends) +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b; +// CHECK-NEXT: 5: UV +// CHECK-NEXT: 6: [B3.5] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B3.6] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (2): B2 B1 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B2 +void test_return() { + A a; + A b; + if (UV) + return; + A c; +} + +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B1] +// CHECK-NEXT: 1: [B4.6] (Lifetime ends) +// CHECK-NEXT: 2: [B4.2] (Lifetime ends) +// CHECK-NEXT: Preds (2): B2 B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B2.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B4] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: a +// CHECK-NEXT: 4: [B4.3] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 5: [B4.4] (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A b = a; +// CHECK-NEXT: 7: b +// CHECK-NEXT: 8: [B4.7] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 9: [B4.8].operator int +// CHECK-NEXT: 10: [B4.8] +// CHECK-NEXT: 11: [B4.10] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 12: [B4.11] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B4.12] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B3 B2 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_if_implicit_scope() { + A a; + if (A b = a) + A c; + else + A c; +} + +// CHECK: [B9 (ENTRY)] +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B1] +// CHECK-NEXT: 1: [B8.6] (Lifetime ends) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A e; +// CHECK-NEXT: 4: [B1.3] (Lifetime ends) +// CHECK-NEXT: 5: [B8.2] (Lifetime ends) +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B2.2] (Lifetime ends) +// CHECK-NEXT: 4: [B4.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B4.2] (Lifetime ends) +// CHECK-NEXT: 3: [B8.6] (Lifetime ends) +// CHECK-NEXT: 4: [B8.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B4] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: UV +// CHECK-NEXT: 4: [B4.3] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B4.4] +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (2): B3 B2 +// CHECK: [B5] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B5.2] (Lifetime ends) +// CHECK-NEXT: 4: [B7.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B6] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B7.2] (Lifetime ends) +// CHECK-NEXT: 3: [B8.6] (Lifetime ends) +// CHECK-NEXT: 4: [B8.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B7] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: UV +// CHECK-NEXT: 4: [B7.3] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.4] +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: a +// CHECK-NEXT: 4: [B8.3] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 5: [B8.4] (CXXConstructExpr, class A) +// CHECK-NEXT: 6: A b = a; +// CHECK-NEXT: 7: b +// CHECK-NEXT: 8: [B8.7] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 9: [B8.8].operator int +// CHECK-NEXT: 10: [B8.8] +// CHECK-NEXT: 11: [B8.10] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 12: [B8.11] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B8.12] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B7 B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (3): B1 B3 B6 +void test_if_jumps() { + A a; + if (A b = a) { + A c; + if (UV) + return; + A d; + } else { + A c; + if (UV) + return; + A d; + } + A e; +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: [B4.4] (Lifetime ends) +// CHECK-NEXT: 2: [B5.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: 4: [B4.4] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B4.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B4.6].operator int +// CHECK-NEXT: 8: [B4.6] +// CHECK-NEXT: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B4.10] +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 B1 +// CHECK: [B5] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_while_implicit_scope() { + A a; + while (A b = a) + A c; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: [B10.4] (Lifetime ends) +// CHECK-NEXT: 2: (CXXConstructExpr, class A) +// CHECK-NEXT: 3: A e; +// CHECK-NEXT: 4: [B1.3] (Lifetime ends) +// CHECK-NEXT: 5: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: 4: [B9.2] (Lifetime ends) +// CHECK-NEXT: 5: [B10.4] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B9.2] (Lifetime ends) +// CHECK-NEXT: 3: [B10.4] (Lifetime ends) +// CHECK-NEXT: 4: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: 2: [B10.4] (Lifetime ends) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B7] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B9] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: UV +// CHECK-NEXT: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.4] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B8 B7 +// CHECK: [B10] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B10.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B10.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B10.6].operator int +// CHECK-NEXT: 8: [B10.6] +// CHECK-NEXT: 9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: while [B10.10] +// CHECK-NEXT: Preds (2): B2 B11 +// CHECK-NEXT: Succs (2): B9 B1 +// CHECK: [B11] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_while_jumps() { + A a; + while (A b = a) { + A c; + if (UV) + break; + if (UV) + continue; + if (UV) + return; + A d; + } + A e; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: [B1.2] (Lifetime ends) +// CHECK-NEXT: 4: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (2): B8 B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: do ... while [B2.2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (2): B10 B1 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: 4: [B9.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B9.2] (Lifetime ends) +// CHECK-NEXT: 3: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B7] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B9] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A b; +// CHECK-NEXT: 3: UV +// CHECK-NEXT: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.4] +// CHECK-NEXT: Preds (2): B10 B11 +// CHECK-NEXT: Succs (2): B8 B7 +// CHECK: [B10] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B9 +// CHECK: [B11] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B9 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_do_jumps() { + A a; + do { + A b; + if (UV) + break; + if (UV) + continue; + if (UV) + return; + A c; + } while (UV); + A d; +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B1] +// CHECK-NEXT: 1: [B4.4] (Lifetime ends) +// CHECK-NEXT: 2: [B5.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A c; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: 4: [B4.4] (Lifetime ends) +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B4.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B4.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B4.6].operator int +// CHECK-NEXT: 8: [B4.6] +// CHECK-NEXT: 9: [B4.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B4.10]; ) +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 B1 +// CHECK: [B5] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_for_implicit_scope() { + for (A a; A b = a;) + A c; +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 +// CHECK: [B1] +// CHECK-NEXT: 1: [B10.4] (Lifetime ends) +// CHECK-NEXT: 2: [B11.4] (Lifetime ends) +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A f; +// CHECK-NEXT: 5: [B1.4] (Lifetime ends) +// CHECK-NEXT: 6: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (2): B3 B6 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B3] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A e; +// CHECK-NEXT: 3: [B3.2] (Lifetime ends) +// CHECK-NEXT: 4: [B9.2] (Lifetime ends) +// CHECK-NEXT: 5: [B10.4] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: return; +// CHECK-NEXT: 2: [B9.2] (Lifetime ends) +// CHECK-NEXT: 3: [B10.4] (Lifetime ends) +// CHECK-NEXT: 4: [B11.4] (Lifetime ends) +// CHECK-NEXT: 5: [B11.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B5] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B5.2] +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (2): B4 B3 +// CHECK: [B6] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: T: continue; +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B7] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B7.2] +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (2): B6 B5 +// CHECK: [B8] +// CHECK-NEXT: 1: [B9.2] (Lifetime ends) +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B9] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A d; +// CHECK-NEXT: 3: UV +// CHECK-NEXT: 4: [B9.3] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: if [B9.4] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B8 B7 +// CHECK: [B10] +// CHECK-NEXT: 1: b +// CHECK-NEXT: 2: [B10.1] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 3: [B10.2] (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A c = b; +// CHECK-NEXT: 5: c +// CHECK-NEXT: 6: [B10.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B10.6].operator int +// CHECK-NEXT: 8: [B10.6] +// CHECK-NEXT: 9: [B10.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B10.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B10.10]; ) +// CHECK-NEXT: Preds (2): B2 B11 +// CHECK-NEXT: Succs (2): B9 B1 +// CHECK: [B11] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: (CXXConstructExpr, class A) +// CHECK-NEXT: 4: A b; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (2): B1 B4 +void test_for_jumps() { + A a; + for (A b; A c = b;) { + A d; + if (UV) + break; + if (UV) + continue; + if (UV) + return; + A e; + } + A f; +} + +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// CHECK-NEXT: 1: (CXXConstructExpr, class A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: 3: int n; +// CHECK-NEXT: 4: n +// CHECK-NEXT: 5: &[B1.4] +// CHECK-NEXT: 6: a +// CHECK-NEXT: 7: [B1.6].p +// CHECK-NEXT: 8: [B1.7] = [B1.5] +// CHECK-NEXT: 9: [B1.2] (Lifetime ends) +// CHECK-NEXT: 10: [B1.3] (Lifetime ends) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_trivial_vs_non_trivial_order() { + A a; + int n; + a.p = &n; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B1] +// CHECK-NEXT: a: +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: i +// CHECK-NEXT: 3: [B1.2] = [B1.1] +// CHECK-NEXT: 4: [B2.1] (Lifetime ends) +// CHECK-NEXT: Preds (2): B2 B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: int i; +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: T: goto a; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void goto_past_declaration() { + goto a; + int i; +a: + i = 1; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B1] +// CHECK-NEXT: a: +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: k +// CHECK-NEXT: 3: [B1.2] = [B1.1] +// CHECK-NEXT: 4: [B2.4] (Lifetime ends) +// CHECK-NEXT: Preds (2): B2 B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: int j; +// CHECK-NEXT: 2: [B2.1] (Lifetime ends) +// CHECK-NEXT: 3: [B3.1] (Lifetime ends) +// CHECK-NEXT: 4: int k; +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B3] +// CHECK-NEXT: 1: int i; +// CHECK-NEXT: 2: [B3.1] (Lifetime ends) +// CHECK-NEXT: T: goto a; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void goto_past_declaration2() { + { + int i; + goto a; + int j; + } + { + int k; + a: + k = 1; + } +} + +struct B { + ~B(); +}; + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B1] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B1.1]++ +// CHECK-NEXT: 3: [B2.2] (Lifetime ends) +// CHECK-NEXT: 4: [B3.1] (Lifetime ends) +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: label: +// CHECK-NEXT: 1: (CXXConstructExpr, struct B) +// CHECK-NEXT: 2: B b; +// CHECK-NEXT: 3: [B2.2] (Lifetime ends) +// CHECK-NEXT: T: goto label; +// CHECK-NEXT: Preds (2): B3 B2 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B3] +// CHECK-NEXT: 1: int i; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +int backpatched_goto() { + int i; +label: + B b; + goto label; + i++; +} diff --git a/test/Analysis/lit.local.cfg b/test/Analysis/lit.local.cfg index acae2bdcd4..a594c5dada 100644 --- a/test/Analysis/lit.local.cfg +++ b/test/Analysis/lit.local.cfg @@ -1,30 +1,13 @@ -import lit.TestRunner -import sys +# -*- Python -*- vim: set ft=python ts=4 sw=4 expandtab tw=79: -# Custom format class for static analyzer tests -class AnalyzerTest(lit.formats.ShTest, object): +import site - def execute(self, test, litConfig): - result = self.executeWithAnalyzeSubstitution(test, litConfig, '-analyzer-constraints=range') - - if result.code == lit.Test.FAIL: - return result - - return result - - def executeWithAnalyzeSubstitution(self, test, litConfig, substitution): - saved_substitutions = list(test.config.substitutions) - test.config.substitutions.append(('%analyze', substitution)) - result = lit.TestRunner.executeShTest(test, litConfig, self.execute_external) - test.config.substitutions = saved_substitutions - - return result - -# This results in a pickling-related failure on Windows -if (not sys.platform in ['win32']): - config.test_format = AnalyzerTest(config.test_format.execute_external) -else: - config.substitutions.append(('%analyze', '-analyzer-constraints=range')) +# Load the custom analyzer test format, which runs the test again with Z3 if it +# is available. +site.addsitedir(os.path.dirname(__file__)) +import analyzer_test +config.test_format = analyzer_test.AnalyzerTest( + config.test_format.execute_external) if not config.root.clang_staticanalyzer: config.unsupported = True diff --git a/test/Analysis/localization-aggressive.m b/test/Analysis/localization-aggressive.m index 346cf3ef22..ea5e0b1529 100644 --- a/test/Analysis/localization-aggressive.m +++ b/test/Analysis/localization-aggressive.m @@ -61,8 +61,16 @@ int random(); NSString *CFNumberFormatterCreateStringWithNumber(float x); + (NSString *)forceLocalized:(NSString *)str __attribute__((annotate("returns_localized_nsstring"))); ++ (NSString *)takesLocalizedString: + (NSString *)__attribute__((annotate("takes_localized_nsstring")))str; @end +NSString * +takesLocalizedString(NSString *str + __attribute__((annotate("takes_localized_nsstring")))) { + return str; +} + // Test cases begin here @implementation LocalizationTestSuite @@ -75,6 +83,8 @@ NSString *ForceLocalized(NSString *str) { return str; } return str; } ++ (NSString *) takesLocalizedString:(NSString *)str { return str; } + // An ObjC method that returns a localized string + (NSString *)unLocalizedStringMethod { return @"UnlocalizedString"; @@ -269,4 +279,13 @@ NSString *ForceLocalized(NSString *str) { return str; } NSString *string2 = POSSIBLE_FALSE_POSITIVE(@"Hello", @"Hello"); // no-warning } +- (void)testTakesLocalizedString { + NSString *localized = NSLocalizedString(@"Hello", @"World"); + NSString *alsoLocalized = [LocalizationTestSuite takesLocalizedString:localized]; // no-warning + NSString *stillLocalized = [LocalizationTestSuite takesLocalizedString:alsoLocalized]; // no-warning + takesLocalizedString(stillLocalized); // no-warning + + [LocalizationTestSuite takesLocalizedString:@"not localized"]; // expected-warning {{User-facing text should use localized string macro}} + takesLocalizedString(@"not localized"); // expected-warning {{User-facing text should use localized string macro}} +} @end diff --git a/test/Analysis/loop-unrolling.cpp b/test/Analysis/loop-unrolling.cpp new file mode 100644 index 0000000000..844d1f18ea --- /dev/null +++ b/test/Analysis/loop-unrolling.cpp @@ -0,0 +1,381 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config unroll-loops=true,cfg-loopexit=true -verify -std=c++11 %s + +void clang_analyzer_numTimesReached(); +void clang_analyzer_warnIfReached(); + +int getNum(); +void foo(int &); + +int simple_unroll1() { + int a[9]; + int k = 42; + for (int i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{9}} + a[i] = 42; + } + int b = 22 / (k - 42); // expected-warning {{Division by zero}} + return 0; +} + +int simple_unroll2() { + int a[9]; + int k = 42; + int i; + for (i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{9}} + a[i] = 42; + } + + for (int j = 0; j <= 9; ++j) { + clang_analyzer_numTimesReached(); // expected-warning {{10}} + a[j] = 42; + } + + int b = 22 / (k - 42); // expected-warning {{Division by zero}} + return 0; +} + +int simple_no_unroll1() { + int a[9]; + int k = 42; + for (int i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + a[i] = 42; + foo(i); + } + int b = 22 / (k - 42); // expected-warning {{Division by zero}} + return 0; +} + +int simple_no_unroll2() { + int a[9]; + int k = 42; + int i; + for (i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + a[i] = 42; + i += getNum(); + } + int b = 22 / (k - 42); // expected-warning {{Division by zero}} + return 0; +} + +int simple_no_unroll3() { + int a[9]; + int k = 42; + for (int i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + a[i] = 42; + (void)&i; + } + int b = 22 / (k - 42); // no-warning + return 0; +} + +int simple_no_unroll4() { + int a[9]; + int k = 42; + int i; + for (i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + a[i] = 42; + int &j = i; + } + int b = 22 / (k - 42); // no-warning + return 0; +} + +int simple_no_unroll5() { + int a[9]; + int k = 42; + int i; + for (i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + a[i] = 42; + int &j{i}; + } + int b = 22 / (k - 42); // no-warning + return 0; +} + +int make_new_branches_loop_cached() { + for (int i = 0; i < 8; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + if(getNum()){ + (void) i; // Since this Stmt does not change the State the analyzer + // won't make a new execution path but reuse the earlier nodes. + } + } + clang_analyzer_warnIfReached(); // no-warning + return 0; +} + +int make_new_branches_loop_uncached() { + int l = 2; + for (int i = 0; i < 8; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{10}} + if(getNum()){ + ++l; + } + } + clang_analyzer_warnIfReached(); // no-warning + return 0; +} + +int make_new_branches_loop_uncached2() { + int l = 2; + for (int i = 0; i < 8; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{10}} + if(getNum()){ + ++l; + } + (void)&i; // This ensures that the loop won't be unrolled. + } + clang_analyzer_warnIfReached(); // no-warning + return 0; +} + + +int escape_before_loop_no_unroll1() { + int a[9]; + int k = 42; + int i; + int &j = i; + for (i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + a[i] = 42; + } + int b = 22 / (k - 42); // no-warning + return 0; +} + +int escape_before_loop_no_unroll2() { + int a[9]; + int k = 42; + int i; + int *p = &i; + for (i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + a[i] = 42; + } + int b = 22 / (k - 42); // no-warning + return 0; +} + +int escape_before_loop_no_unroll3() { + int a[9]; + int k = 42; + int i; + foo(i); + for (i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + a[i] = 42; + } + int b = 22 / (k - 42); // no-warning + return 0; +} + +int nested_outer_unrolled() { + int a[9]; + int k = 42; + int j = 0; + for (int i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{1}} + for (j = 0; j < 9; ++j) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + a[j] = 22; + (void) &j; // ensures that the inner loop won't be unrolled + } + a[i] = 42; + } + int b = 22 / (k - 42); // no-warning + return 0; +} + +int nested_inner_unrolled() { + int a[9]; + int k = 42; + int j = 0; + for (int i = 0; i < getNum(); i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + for (j = 0; j < 8; ++j) { + clang_analyzer_numTimesReached(); // expected-warning {{32}} + a[j] = 22; + } + a[i] = 42; + } + int b = 22 / (k - 42); // expected-warning {{Division by zero}} + return 0; +} + +int nested_both_unrolled() { + int a[9]; + int k = 42; + int j = 0; + for (int i = 0; i < 7; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{7}} + for (j = 0; j < 6; ++j) { + clang_analyzer_numTimesReached(); // expected-warning {{42}} + a[j] = 22; + } + a[i] = 42; + } + int b = 22 / (k - 42); // expected-warning {{Division by zero}} + return 0; +} + +int simple_known_bound_loop() { + for (int i = 2; i < 12; i++) { + // This function is inlined in nested_inlined_unroll1() + clang_analyzer_numTimesReached(); // expected-warning {{90}} + } + return 0; +} + +int simple_unknown_bound_loop() { + for (int i = 2; i < getNum(); i++) { + clang_analyzer_numTimesReached(); // expected-warning {{10}} + } + return 0; +} + +int nested_inlined_unroll1() { + int k; + for (int i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{9}} + k = simple_known_bound_loop(); // no reevaluation without inlining + } + int a = 22 / k; // expected-warning {{Division by zero}} + return 0; +} + +int nested_inlined_no_unroll1() { + int k; + for (int i = 0; i < 9; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{15}} + k = simple_unknown_bound_loop(); // reevaluation without inlining, splits the state as well + } + int a = 22 / k; // no-warning + return 0; +} + +int recursion_unroll1(bool b) { + int k = 2; + for (int i = 0; i < 5; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{13}} + if(i == 0 && b) // Splits the state in the first iteration but the recursion + // call will be unrolled anyway since the condition is known there. + recursion_unroll1(false); + clang_analyzer_numTimesReached(); // expected-warning {{14}} + } + int a = 22 / k; // no-warning + return 0; +} + +int recursion_unroll2(bool b) { + int k = 0; + for (int i = 0; i < 5; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{9}} + if(i == 0 && b) + recursion_unroll2(false); + clang_analyzer_numTimesReached(); // expected-warning {{9}} + } + int a = 22 / k; // expected-warning {{Division by zero}} + return 0; +} + +int recursion_unroll3(bool b) { + int k = 2; + for (int i = 0; i < 5; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{10}} + if (i == 4 && b) { + recursion_unroll3(false); + break; + } + clang_analyzer_numTimesReached(); // expected-warning {{10}} + } + int a = 22 / k; + return 0; +} + +int recursion_unroll4(bool b) { + int k = 2; + for (int i = 0; i < 5; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{13}} + if(i == 0 && b) { + recursion_unroll4(false); + continue; + } + clang_analyzer_numTimesReached(); // expected-warning {{13}} + } + int a = 22 / k; + return 0; +} + +int loop_exit_while_empty_loop_stack() { + if (getNum()) + for (int i = 1; i < 8; i++) + ; + return 0; +} + +int num_steps_on_limit() { + for (int i = 0; i < 128; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{128}} + } + clang_analyzer_numTimesReached(); // expected-warning {{1}} + return 0; +} + +int num_steps_over_limit1() { + for (int i = 0; i < 129; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + } + return 0; +} + +int num_steps_on_limit2() { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 64; j++) { + clang_analyzer_numTimesReached(); // expected-warning {{128}} + } + } + return 0; +} + +int num_steps_over_limit2() { + for (int i = 0; i < 2; i++) { + clang_analyzer_numTimesReached(); // expected-warning {{1}} + for (int j = 0; j <= 64; j++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + } + } + return 0; +} + +int num_steps_on_limit3() { + for (int i = 0; i < getNum(); i++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + for (int j = 0; j < 32; j++) { + clang_analyzer_numTimesReached(); // expected-warning {{128}} + } + } + return 0; +} + +int num_steps_over_limit3() { + for (int i = 0; i < getNum(); i++) { + clang_analyzer_numTimesReached(); // expected-warning {{1}} + for (int j = 0; j < 33; j++) { + clang_analyzer_numTimesReached(); // expected-warning {{4}} + } + } + return 0; +} + + +void pr34943() { + for (int i = 0; i < 6L; ++i) { + clang_analyzer_numTimesReached(); // expected-warning {{6}} + } +} diff --git a/test/Analysis/loopexit-cfg-output.cpp b/test/Analysis/loopexit-cfg-output.cpp new file mode 100644 index 0000000000..8e53ce3066 --- /dev/null +++ b/test/Analysis/loopexit-cfg-output.cpp @@ -0,0 +1,476 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=debug.DumpCFG -analyzer-config cfg-loopexit=true %s > %t 2>&1 +// RUN: FileCheck --input-file=%t %s + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 + +// CHECK: [B1] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B2.1]++ +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B3] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B3.1]++ +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B4] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 12 +// CHECK-NEXT: 4: [B4.2] < [B4.3] +// CHECK-NEXT: T: for (...; [B4.4]; ...) +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 B1 + +// CHECK: [B5] +// CHECK-NEXT: 1: 0 +// CHECK-NEXT: 2: int i = 0; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_forloop1() { + for (int i = 0; i < 12; i++) { + i++; + } + return; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B1] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B3] +// CHECK-NEXT: T: for (; ; ) +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (2): B2 NULL + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_forloop2() { + for (;;) + ; +} + +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B3] +// CHECK-NEXT: 1: int i; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B4] +// CHECK-NEXT: 1: true +// CHECK-NEXT: T: while [B4.1] +// CHECK-NEXT: Preds (2): B2 B5 +// CHECK-NEXT: Succs (2): B3 NULL + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_while1() { + while (true) { + int i; + } +} + +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: 2 +// CHECK-NEXT: 3: int k = 2; +// CHECK-NEXT: 4: return; +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B3] +// CHECK-NEXT: 1: l +// CHECK-NEXT: 2: [B3.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 42 +// CHECK-NEXT: 4: [B3.2] < [B3.3] +// CHECK-NEXT: T: while [B3.4] +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (2): B2 B1 + +// CHECK: [B4] +// CHECK-NEXT: 1: int l; +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_while2() { + int l; + while (l < 42) + ; + int k = 2; + return; +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B3] +// CHECK-NEXT: 1: false +// CHECK-NEXT: T: while [B3.1] +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (2): NULL B1 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_while3() { + while (false) { + ; + } +} + +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B1] +// CHECK-NEXT: 1: DoStmt (LoopExit) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: 1: false +// CHECK-NEXT: T: do ... while [B2.1] +// CHECK-NEXT: Preds (2): B3 B4 +// CHECK-NEXT: Succs (2): NULL B1 + +// CHECK: [B3] +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_dowhile1() { + do { + } while (false); +} + +// CHECK: [B6 (ENTRY)] +// CHECK-NEXT: Succs (1): B5 + +// CHECK: [B1] +// CHECK-NEXT: 1: DoStmt (LoopExit) +// CHECK-NEXT: 2: j +// CHECK-NEXT: 3: [B1.2]-- +// CHECK-NEXT: 4: return; +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 20 +// CHECK-NEXT: 4: [B2.2] < [B2.3] +// CHECK-NEXT: T: do ... while [B2.4] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (2): B4 B1 + +// CHECK: [B3] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: 2 +// CHECK-NEXT: 3: [B3.1] += [B3.2] +// CHECK-NEXT: Preds (2): B4 B5 +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B4] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B5] +// CHECK-NEXT: 1: 2 +// CHECK-NEXT: 2: int j = 2; +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B3 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_dowhile2() { + int j = 2; + do { + j += 2; + } while (j < 20); + j--; + return; +} + +// CHECK: [B10 (ENTRY)] +// CHECK-NEXT: Succs (1): B9 + +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B8 + +// CHECK: [B3] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B4] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: [B4.1]++ +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B6 + +// CHECK: [B5] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B5.1]++ +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B6] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: [B6.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 6 +// CHECK-NEXT: 4: [B6.2] < [B6.3] +// CHECK-NEXT: T: for (...; [B6.4]; ...) +// CHECK-NEXT: Preds (2): B4 B7 +// CHECK-NEXT: Succs (2): B5 B3 + +// CHECK: [B7] +// CHECK-NEXT: 1: 1 +// CHECK-NEXT: 2: int j = 1; +// CHECK-NEXT: Preds (1): B8 +// CHECK-NEXT: Succs (1): B6 + +// CHECK: [B8] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 2 +// CHECK-NEXT: 4: [B8.2] < [B8.3] +// CHECK-NEXT: T: while [B8.4] +// CHECK-NEXT: Preds (2): B2 B9 +// CHECK-NEXT: Succs (2): B7 B1 + +// CHECK: [B9] +// CHECK-NEXT: 1: 40 +// CHECK-NEXT: 2: -[B9.1] +// CHECK-NEXT: 3: int i = -40; +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (1): B8 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void nested_loops1() { + int i = -40; + while (i < 2) { + for (int j = 1; j < 6; j++) + i++; + } +} + +// CHECK: [B9 (ENTRY)] +// CHECK-NEXT: Succs (1): B8 + +// CHECK: [B1] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: [B2.1]++ +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B7 + +// CHECK: [B3] +// CHECK-NEXT: 1: DoStmt (LoopExit) +// CHECK-NEXT: 2: i +// CHECK-NEXT: 3: [B3.2]-- +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B2 + +// CHECK: [B4] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 2 +// CHECK-NEXT: 4: [B4.2] < [B4.3] +// CHECK-NEXT: T: do ... while [B4.4] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B6 B3 + +// CHECK: [B5] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B5.1]++ +// CHECK-NEXT: Preds (2): B6 B7 +// CHECK-NEXT: Succs (1): B4 + +// CHECK: [B6] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B5 + +// CHECK: [B7] +// CHECK-NEXT: 1: j +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 6 +// CHECK-NEXT: 4: [B7.2] < [B7.3] +// CHECK-NEXT: T: for (...; [B7.4]; ...) +// CHECK-NEXT: Preds (2): B2 B8 +// CHECK-NEXT: Succs (2): B5 B1 + +// CHECK: [B8] +// CHECK-NEXT: 1: 40 +// CHECK-NEXT: 2: -[B8.1] +// CHECK-NEXT: 3: int i = -40; +// CHECK-NEXT: 4: 1 +// CHECK-NEXT: 5: int j = 1; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B7 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void nested_loops2() { + int i = -40; + for (int j = 1; j < 6; j++) { + do { + i++; + } while (i < 2); + i--; + } +} + +// CHECK: [B12 (ENTRY)] +// CHECK-NEXT: Succs (1): B11 + +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: return; +// CHECK-NEXT: Preds (2): B3 B5 +// CHECK-NEXT: Succs (1): B0 + +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B5 + +// CHECK: [B3] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B4 +// CHECK-NEXT: Succs (1): B1 + +// CHECK: [B4] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B4.1]++ +// CHECK-NEXT: 3: i +// CHECK-NEXT: 4: [B4.3] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 5: 2 +// CHECK-NEXT: 6: [B4.4] % [B4.5] +// CHECK-NEXT: 7: [B4.6] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: if [B4.7] +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (2): B3 B2 + +// CHECK: [B5] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B5.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 5 +// CHECK-NEXT: 4: [B5.2] < [B5.3] +// CHECK-NEXT: T: while [B5.4] +// CHECK-NEXT: Preds (2): B2 B6 +// CHECK-NEXT: Succs (2): B4 B1 + +// CHECK: [B6] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: 1 +// CHECK-NEXT: 3: int i = 1; +// CHECK-NEXT: Preds (2): B8 B10 +// CHECK-NEXT: Succs (1): B5 + +// CHECK: [B7] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B7.1]++ +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B10 + +// CHECK: [B8] +// CHECK-NEXT: T: break; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B6 + +// CHECK: [B9] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 4 +// CHECK-NEXT: 4: [B9.2] == [B9.3] +// CHECK-NEXT: T: if [B9.4] +// CHECK-NEXT: Preds (1): B10 +// CHECK-NEXT: Succs (2): B8 B7 + +// CHECK: [B10] +// CHECK-NEXT: 1: i +// CHECK-NEXT: 2: [B10.1] (ImplicitCastExpr, LValueToRValue, int) +// CHECK-NEXT: 3: 6 +// CHECK-NEXT: 4: [B10.2] < [B10.3] +// CHECK-NEXT: T: for (...; [B10.4]; ...) +// CHECK-NEXT: Preds (2): B7 B11 +// CHECK-NEXT: Succs (2): B9 B6 + +// CHECK: [B11] +// CHECK-NEXT: 1: 2 +// CHECK-NEXT: 2: int i = 2; +// CHECK-NEXT: Preds (1): B12 +// CHECK-NEXT: Succs (1): B10 + +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void check_break() +{ + for(int i = 2; i < 6; i++) { + if(i == 4) + break; + } + + int i = 1; + while(i<5){ + i++; + if(i%2) + break; + } + + return; +} diff --git a/test/Analysis/malloc-overflow2.c b/test/Analysis/malloc-overflow2.c index 2e1b1d4d2b..7c580602e6 100644 --- a/test/Analysis/malloc-overflow2.c +++ b/test/Analysis/malloc-overflow2.c @@ -1,4 +1,5 @@ // RUN: %clang_analyze_cc1 -triple x86_64-unknown-unknown -analyzer-checker=alpha.security.MallocOverflow,unix -verify %s +// RUN: %clang_analyze_cc1 -triple x86_64-unknown-unknown -analyzer-checker=alpha.security.MallocOverflow,unix,optin.portability -DPORTABILITY -verify %s typedef __typeof__(sizeof(int)) size_t; extern void *malloc(size_t); @@ -32,5 +33,8 @@ static int table_build_1(struct table *t) { } void *f(int n) { - return malloc(n * 0 * sizeof(int)); // expected-warning {{Call to 'malloc' has an allocation size of 0 bytes}} + return malloc(n * 0 * sizeof(int)); +#ifdef PORTABILITY + // expected-warning@-2{{Call to 'malloc' has an allocation size of 0 bytes}} +#endif } diff --git a/test/Analysis/malloc-plist.c b/test/Analysis/malloc-plist.c index 26aea16045..e2062e8582 100644 --- a/test/Analysis/malloc-plist.c +++ b/test/Analysis/malloc-plist.c @@ -421,7 +421,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'p'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -586,7 +586,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'A'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -974,7 +974,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'buf'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -1376,7 +1376,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'buf'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -1962,7 +1962,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Use of memory after it is freed</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Use-after-free</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -2524,7 +2524,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'buf'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -2795,7 +2795,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'v'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -3144,7 +3144,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Use of memory after it is freed</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Use-after-free</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -3309,7 +3309,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'm'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -3517,7 +3517,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'x'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -3725,7 +3725,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'x'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -4030,7 +4030,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'x'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -4335,7 +4335,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'x'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -4543,7 +4543,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'x'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -4751,7 +4751,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'x'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -4988,7 +4988,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential memory leak</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -5225,7 +5225,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential memory leak</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -5496,7 +5496,7 @@ void testMyMalloc() { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential memory leak</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c index d5f2cfedd1..4c364ebd9a 100644 --- a/test/Analysis/malloc.c +++ b/test/Analysis/malloc.c @@ -1774,6 +1774,16 @@ int testNoCheckerDataPropogationFromLogicalOpOperandToOpResult(void) { return ok; // no warning } +void (*fnptr)(int); +void freeIndirectFunctionPtr() { + void *p = (void *)fnptr; + free(p); // expected-warning {{Argument to free() is a function pointer}} +} + +void freeFunctionPtr() { + free((void *)fnptr); // expected-warning {{Argument to free() is a function pointer}} +} + // ---------------------------------------------------------------------------- // False negatives. diff --git a/test/Analysis/malloc.mm b/test/Analysis/malloc.mm index f8a43b3b6a..e3daa858be 100644 --- a/test/Analysis/malloc.mm +++ b/test/Analysis/malloc.mm @@ -187,7 +187,7 @@ typedef volatile struct { void *opaque1; long opaque2; } OSQueueHead; -void OSAtomicEnqueue( OSQueueHead *__list, void *__new, size_t __offset) __attribute__((weak_import)); +extern "C" void OSAtomicEnqueue( OSQueueHead *__list, void *__new, size_t __offset) __attribute__((weak_import)); static inline void radar11111210(OSQueueHead *pool) { void *newItem = malloc(4); OSAtomicEnqueue(pool, newItem, 4); diff --git a/test/Analysis/max-nodes-suppress-on-sink.c b/test/Analysis/max-nodes-suppress-on-sink.c index 8d955b91c1..da1e7bf022 100644 --- a/test/Analysis/max-nodes-suppress-on-sink.c +++ b/test/Analysis/max-nodes-suppress-on-sink.c @@ -15,6 +15,8 @@ extern void exit(int) __attribute__ ((__noreturn__)); void clang_analyzer_warnIfReached(void); +int coin(); + void test_single_cfg_block_sink() { void *p = malloc(1); // no-warning (wherever the leak warning may occur here) @@ -29,3 +31,53 @@ void test_single_cfg_block_sink() { // the leak report. exit(0); } + +// A similar test with more complicated control flow before the no-return thing, +// so that the no-return thing wasn't in the same CFG block. +void test_more_complex_control_flow_before_sink() { + void *p = malloc(1); // no-warning + + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + clang_analyzer_warnIfReached(); // no-warning + + if (coin()) + exit(0); + else + exit(1); +} + +// A loop before the no-return function, to make sure that +// the dominated-by-sink analysis doesn't hang. +void test_loop_before_sink(int n) { + void *p = malloc(1); // no-warning + + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + clang_analyzer_warnIfReached(); // no-warning + + for (int i = 0; i < n; ++i) { + } + exit(1); +} + +// We're not sure if this is no-return. +void test_loop_with_sink(int n) { + void *p = malloc(1); // expected-warning@+2{{Potential leak of memory}} + + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + clang_analyzer_warnIfReached(); // no-warning + + for (int i = 0; i < n; ++i) + if (i == 0) + exit(1); +} + +// Handle unreachable blocks correctly. +void test_unreachable_successor_blocks() { + void *p = malloc(1); // no-warning + + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + clang_analyzer_warnIfReached(); // no-warning + + if (1) // no-crash + exit(1); +} diff --git a/test/Analysis/max-nodes-suppress-on-sink.cpp b/test/Analysis/max-nodes-suppress-on-sink.cpp new file mode 100644 index 0000000000..814b302789 --- /dev/null +++ b/test/Analysis/max-nodes-suppress-on-sink.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_analyze_cc1 -x c++ -fcxx-exceptions -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config max-nodes=12 -verify %s + +// Here we test how "suppress on sink" feature of certain bugtypes interacts +// with reaching analysis limits. See comments in max-nodes-suppress-on-sink.c +// for more discussion. + +typedef __typeof(sizeof(int)) size_t; +void *malloc(size_t); + +void clang_analyzer_warnIfReached(void); + +// Because we don't have a better approach, we currently treat throw as +// noreturn. +void test_throw_treated_as_noreturn() { + void *p = malloc(1); // no-warning + + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + clang_analyzer_warnIfReached(); // no-warning + + throw 0; +} + +// FIXME: Handled throws shouldn't be suppressing us! +void test_handled_throw_treated_as_noreturn() { + void *p = malloc(1); // no-warning + + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + clang_analyzer_warnIfReached(); // no-warning + + try { + throw 0; + } catch (int i) { + } +} diff --git a/test/Analysis/nonnull-global-constants.mm b/test/Analysis/nonnull-global-constants.mm new file mode 100644 index 0000000000..7900b9dd12 --- /dev/null +++ b/test/Analysis/nonnull-global-constants.mm @@ -0,0 +1,103 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s + +// Nullability of const string-like globals, testing +// NonnullGlobalConstantsChecker. + +void clang_analyzer_eval(bool); + +@class NSString; +typedef const struct __CFString *CFStringRef; +typedef const struct __CFBoolean * CFBooleanRef; + +// Global NSString* is non-null. +extern NSString *const StringConstGlobal; +void stringConstGlobal() { + clang_analyzer_eval(StringConstGlobal); // expected-warning{{TRUE}} +} + +// The logic does not apply to local variables though. +extern NSString *stringGetter(); +void stringConstLocal() { + NSString *const local = stringGetter(); + clang_analyzer_eval(local); // expected-warning{{UNKNOWN}} +} + +// Global const CFStringRef's are also assumed to be non-null. +extern const CFStringRef CFStringConstGlobal; +void cfStringCheckGlobal() { + clang_analyzer_eval(CFStringConstGlobal); // expected-warning{{TRUE}} +} + +// But only "const" ones. +extern CFStringRef CFStringNonConstGlobal; +void cfStringCheckMutableGlobal() { + clang_analyzer_eval(CFStringNonConstGlobal); // expected-warning{{UNKNOWN}} +} + +// char* const is also assumed to be non-null. +extern const char *const ConstCharStarConst; +void constCharStarCheckGlobal() { + clang_analyzer_eval(ConstCharStarConst); // expected-warning{{TRUE}} +} + +// Pointer value can be mutable. +extern char *const CharStarConst; +void charStarCheckGlobal() { + clang_analyzer_eval(CharStarConst); // expected-warning{{TRUE}} +} + +// But the pointer itself should be immutable. +extern char *CharStar; +void charStartCheckMutableGlobal() { + clang_analyzer_eval(CharStar); // expected-warning{{UNKNOWN}} +} + +// Type definitions should also work across typedefs, for pointers: +typedef char *const str; +extern str globalStr; +void charStarCheckTypedef() { + clang_analyzer_eval(globalStr); // expected-warning{{TRUE}} +} + +// And for types. +typedef NSString *const NStr; +extern NStr globalNSString; +void NSStringCheckTypedef() { + clang_analyzer_eval(globalNSString); // expected-warning{{TRUE}} +} + +// Note that constness could be either inside +// the var declaration, or in a typedef. +typedef NSString *NStr2; +extern const NStr2 globalNSString2; +void NSStringCheckConstTypedef() { + clang_analyzer_eval(globalNSString2); // expected-warning{{TRUE}} +} + +// Nested typedefs should work as well. +typedef const CFStringRef str1; +typedef str1 str2; +extern str2 globalStr2; +void testNestedTypedefs() { + clang_analyzer_eval(globalStr2); // expected-warning{{TRUE}} +} + +// And for NSString *. +typedef NSString *const nstr1; +typedef nstr1 nstr2; +extern nstr2 nglobalStr2; +void testNestedTypedefsForNSString() { + clang_analyzer_eval(nglobalStr2); // expected-warning{{TRUE}} +} + +// And for CFBooleanRefs. +extern const CFBooleanRef kBool; +void testNonnullBool() { + clang_analyzer_eval(kBool); // expected-warning{{TRUE}} +} + +// And again, only for const one. +extern CFBooleanRef kBoolMutable; +void testNonnullNonconstBool() { + clang_analyzer_eval(kBoolMutable); // expected-warning{{UNKNOWN}} +} diff --git a/test/Analysis/null-deref-offsets.c b/test/Analysis/null-deref-offsets.c new file mode 100644 index 0000000000..988cec4985 --- /dev/null +++ b/test/Analysis/null-deref-offsets.c @@ -0,0 +1,37 @@ +// RUN: %clang_analyze_cc1 -w -triple i386-apple-darwin10 -analyzer-checker=core,debug.ExprInspection -verify %s + +void clang_analyzer_eval(int); + +struct S { + int x, y; + int z[2]; +}; + +void testOffsets(struct S *s, int coin) { + if (s != 0) + return; + + // FIXME: Here we are testing the hack that computes offsets to null pointers + // as 0 in order to find null dereferences of not-exactly-null pointers, + // such as &(s->y) below, which is equal to 4 rather than 0 in run-time. + + // These are indeed null. + clang_analyzer_eval(s == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(&(s->x) == 0); // expected-warning{{TRUE}} + + // FIXME: These should ideally be true. + clang_analyzer_eval(&(s->y) == 4); // expected-warning{{FALSE}} + clang_analyzer_eval(&(s->z[0]) == 8); // expected-warning{{FALSE}} + clang_analyzer_eval(&(s->z[1]) == 12); // expected-warning{{FALSE}} + + // FIXME: These should ideally be false. + clang_analyzer_eval(&(s->y) == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(&(s->z[0]) == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(&(s->z[1]) == 0); // expected-warning{{TRUE}} + + // But these should still be reported as null dereferences. + if (coin) + s->y = 5; // expected-warning{{Access to field 'y' results in a dereference of a null pointer (loaded from variable 's')}} + else + s->z[1] = 6; // expected-warning{{Array access (via field 'z') results in a null pointer dereference}} +} diff --git a/test/Analysis/null-deref-path-notes.c b/test/Analysis/null-deref-path-notes.c new file mode 100644 index 0000000000..a1477a6226 --- /dev/null +++ b/test/Analysis/null-deref-path-notes.c @@ -0,0 +1,9 @@ +// RUN: %clang_analyze_cc1 -w -x c -analyzer-checker=core -analyzer-output=text -verify %s + +// Avoid the crash when finding the expression for tracking the origins +// of the null pointer for path notes. +void pr34373() { + int *a = 0; // expected-note{{'a' initialized to a null pointer value}} + (a + 0)[0]; // expected-warning{{Array access results in a null pointer dereference}} + // expected-note@-1{{Array access results in a null pointer dereference}} +} diff --git a/test/Analysis/null-deref-path-notes.cpp b/test/Analysis/null-deref-path-notes.cpp new file mode 100644 index 0000000000..617f5def15 --- /dev/null +++ b/test/Analysis/null-deref-path-notes.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_analyze_cc1 -w -x c++ -analyzer-checker=core -analyzer-output=text -analyzer-eagerly-assume -verify %s + +namespace pr34731 { +int b; +class c { + class B { + public: + double ***d; + B(); + }; + void e(double **, int); + void f(B &, int &); +}; + +// Properly track the null pointer in the array field back to the default +// constructor of 'h'. +void c::f(B &g, int &i) { + e(g.d[9], i); // expected-warning{{Array access (via field 'd') results in a null pointer dereference}} + // expected-note@-1{{Array access (via field 'd') results in a null pointer dereference}} + B h, a; // expected-note{{Value assigned to 'h.d'}} + a.d == __null; // expected-note{{Assuming the condition is true}} + a.d != h.d; // expected-note{{Assuming pointer value is null}} + f(h, b); // expected-note{{Calling 'c::f'}} +} +} diff --git a/test/Analysis/null-deref-path-notes.m b/test/Analysis/null-deref-path-notes.m index 242b5daa9b..39cf9c79f3 100644 --- a/test/Analysis/null-deref-path-notes.m +++ b/test/Analysis/null-deref-path-notes.m @@ -50,6 +50,23 @@ void repeatedStores(int coin) { *p = 1; // expected-warning{{Dereference of null pointer}} expected-note{{Dereference of null pointer}} } +@interface WithArrayPtr +- (void) useArray; +@end + +@implementation WithArrayPtr { +@public int *p; +} +- (void)useArray { + p[1] = 2; // expected-warning{{Array access (via ivar 'p') results in a null pointer dereference}} + // expected-note@-1{{Array access (via ivar 'p') results in a null pointer dereference}} +} +@end + +void testWithArrayPtr(WithArrayPtr *w) { + w->p = 0; // expected-note{{Null pointer value stored to 'p'}} + [w useArray]; // expected-note{{Calling 'useArray'}} +} // CHECK: <key>diagnostics</key> // CHECK-NEXT: <array> @@ -801,4 +818,227 @@ void repeatedStores(int coin) { // CHECK-NEXT: <key>file</key><integer>0</integer> // CHECK-NEXT: </dict> // CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>path</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>event</string> +// CHECK-NEXT: <key>location</key> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>67</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <key>ranges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>67</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>67</integer> +// CHECK-NEXT: <key>col</key><integer>10</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>depth</key><integer>0</integer> +// CHECK-NEXT: <key>extended_message</key> +// CHECK-NEXT: <string>Null pointer value stored to 'p'</string> +// CHECK-NEXT: <key>message</key> +// CHECK-NEXT: <string>Null pointer value stored to 'p'</string> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>control</string> +// CHECK-NEXT: <key>edges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>start</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>67</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>67</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>end</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>68</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>68</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>event</string> +// CHECK-NEXT: <key>location</key> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>68</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <key>ranges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>68</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>68</integer> +// CHECK-NEXT: <key>col</key><integer>14</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>depth</key><integer>0</integer> +// CHECK-NEXT: <key>extended_message</key> +// CHECK-NEXT: <string>Calling 'useArray'</string> +// CHECK-NEXT: <key>message</key> +// CHECK-NEXT: <string>Calling 'useArray'</string> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>event</string> +// CHECK-NEXT: <key>location</key> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>60</integer> +// CHECK-NEXT: <key>col</key><integer>1</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <key>depth</key><integer>1</integer> +// CHECK-NEXT: <key>extended_message</key> +// CHECK-NEXT: <string>Entered call from 'testWithArrayPtr'</string> +// CHECK-NEXT: <key>message</key> +// CHECK-NEXT: <string>Entered call from 'testWithArrayPtr'</string> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>control</string> +// CHECK-NEXT: <key>edges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>start</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>60</integer> +// CHECK-NEXT: <key>col</key><integer>1</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>60</integer> +// CHECK-NEXT: <key>col</key><integer>1</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>end</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>61</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>61</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>control</string> +// CHECK-NEXT: <key>edges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>start</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>61</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>61</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>end</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>61</integer> +// CHECK-NEXT: <key>col</key><integer>8</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>61</integer> +// CHECK-NEXT: <key>col</key><integer>8</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>event</string> +// CHECK-NEXT: <key>location</key> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>61</integer> +// CHECK-NEXT: <key>col</key><integer>8</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <key>ranges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>61</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>61</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>depth</key><integer>1</integer> +// CHECK-NEXT: <key>extended_message</key> +// CHECK-NEXT: <string>Array access (via ivar 'p') results in a null pointer dereference</string> +// CHECK-NEXT: <key>message</key> +// CHECK-NEXT: <string>Array access (via ivar 'p') results in a null pointer dereference</string> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>description</key><string>Array access (via ivar 'p') results in a null pointer dereference</string> +// CHECK-NEXT: <key>category</key><string>Logic error</string> +// CHECK-NEXT: <key>type</key><string>Dereference of null pointer</string> +// CHECK-NEXT: <key>check_name</key><string>core.NullDereference</string> +// CHECK-NEXT: <!-- This hash is experimental and going to change! --> +// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>fb0ad1e4e3090d9834d542eb54bc9d2e</string> +// CHECK-NEXT: <key>issue_context_kind</key><string>Objective-C method</string> +// CHECK-NEXT: <key>issue_context</key><string>useArray</string> +// CHECK-NEXT: <key>issue_hash_function_offset</key><string>1</string> +// CHECK-NEXT: <key>location</key> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>61</integer> +// CHECK-NEXT: <key>col</key><integer>8</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </dict> // CHECK-NEXT: </array> diff --git a/test/Analysis/null-deref-ps-region.c b/test/Analysis/null-deref-ps-region.c index 6ef99ae473..c46ca6c52a 100644 --- a/test/Analysis/null-deref-ps-region.c +++ b/test/Analysis/null-deref-ps-region.c @@ -1,6 +1,11 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core -std=gnu99 -analyzer-store=region -verify %s -// expected-no-diagnostics +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core,unix,alpha.unix -std=gnu99 -analyzer-store=region -verify %s +#include "Inputs/system-header-simulator.h" + +typedef __typeof(sizeof(int)) size_t; +void *memset(void *__s, int __c, size_t __n); +void *malloc(size_t __size); +void free(void *__ptr); // The store for 'a[1]' should not be removed mistakenly. SymbolicRegions may // also be live roots. @@ -13,3 +18,55 @@ void f14(int *a) { i = *p; // no-warning } } + +void foo() { + int *x = malloc(sizeof(int)); + memset(x, 0, sizeof(int)); + int n = 1 / *x; // FIXME: no-warning + free(x); +} + +void bar() { + int *x = malloc(sizeof(int)); + memset(x, 0, 1); + int n = 1 / *x; // no-warning + free(x); +} + +void testConcreteNull() { + int *x = 0; + memset(x, 0, 1); // expected-warning {{Null pointer argument in call to memory set function}} +} + +void testStackArray() { + char buf[13]; + memset(buf, 0, 1); // no-warning +} + +void testHeapSymbol() { + char *buf = (char *)malloc(13); + memset(buf, 0, 1); // no-warning + free(buf); +} + +void testStackArrayOutOfBound() { + char buf[1]; + memset(buf, 0, 1024); // expected-warning {{Memory set function accesses out-of-bound array element}} +} + +void testHeapSymbolOutOfBound() { + char *buf = (char *)malloc(1); + memset(buf, 0, 1024); // expected-warning {{Memory set function accesses out-of-bound array element}} + free(buf); +} + +void testStackArraySameSize() { + char buf[1]; + memset(buf, 0, sizeof(buf)); // no-warning +} + +void testHeapSymbolSameSize() { + char *buf = (char *)malloc(1); + memset(buf, 0, 1); // no-warning + free(buf); +} diff --git a/test/Analysis/null-deref-ps.c b/test/Analysis/null-deref-ps.c index 5dee308a00..d0e1f9f5cc 100644 --- a/test/Analysis/null-deref-ps.c +++ b/test/Analysis/null-deref-ps.c @@ -1,5 +1,5 @@ -// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -analyzer-checker=core,deadcode,alpha.core -std=gnu99 -analyzer-store=region -analyzer-purge=none -verify %s -Wno-error=return-type -// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -analyzer-checker=core,deadcode,alpha.core -std=gnu99 -analyzer-store=region -verify %s -Wno-error=return-type +// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -Wno-tautological-constant-compare -Wtautological-unsigned-zero-compare -analyzer-checker=core,deadcode,alpha.core -std=gnu99 -analyzer-store=region -analyzer-purge=none -verify %s -Wno-error=return-type +// RUN: %clang_analyze_cc1 -triple i386-apple-darwin10 -Wno-tautological-constant-compare -Wtautological-unsigned-zero-compare -analyzer-checker=core,deadcode,alpha.core -std=gnu99 -analyzer-store=region -verify %s -Wno-error=return-type typedef unsigned uintptr_t; diff --git a/test/Analysis/nullability-notes.m b/test/Analysis/nullability-notes.m new file mode 100644 index 0000000000..c93aa02c42 --- /dev/null +++ b/test/Analysis/nullability-notes.m @@ -0,0 +1,204 @@ +// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull,nullability.NullablePassedToNonnull,nullability.NullableReturnedFromNonnull,nullability.NullableDereferenced -analyzer-output=text -verify %s +// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull,nullability.NullablePassedToNonnull,nullability.NullableReturnedFromNonnull,nullability.NullableDereferenced -analyzer-output=plist -analyzer-config path-diagnostics-alternate=true -o %t.plist %s +// RUN: FileCheck --input-file=%t.plist %s + +#include "Inputs/system-header-simulator-for-nullability.h" + +void takesNonnull(NSObject *_Nonnull y); + +@interface ClassWithProperties: NSObject +@property(copy, nullable) NSObject *x; // plist check ensures no control flow piece from here to 'self.x'. +-(void) method; +@end; +@implementation ClassWithProperties +-(void) method { + // no-crash + NSObject *x = self.x; // expected-note{{Nullability 'nullable' is inferred}} + takesNonnull(x); // expected-warning{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} + // expected-note@-1{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} +} +@end + +// CHECK: <key>diagnostics</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>path</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>control</string> +// CHECK-NEXT: <key>edges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>start</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>10</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>end</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>22</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>22</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>event</string> +// CHECK-NEXT: <key>location</key> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>22</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <key>ranges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>22</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>22</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>depth</key><integer>1</integer> +// CHECK-NEXT: <key>extended_message</key> +// CHECK-NEXT: <string>Nullability 'nullable' is inferred</string> +// CHECK-NEXT: <key>message</key> +// CHECK-NEXT: <string>Nullability 'nullable' is inferred</string> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>control</string> +// CHECK-NEXT: <key>edges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>start</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>22</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>22</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>end</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>10</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>control</string> +// CHECK-NEXT: <key>edges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>start</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>16</integer> +// CHECK-NEXT: <key>col</key><integer>10</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>end</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>17</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>17</integer> +// CHECK-NEXT: <key>col</key><integer>14</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>kind</key><string>event</string> +// CHECK-NEXT: <key>location</key> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>17</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <key>ranges</key> +// CHECK-NEXT: <array> +// CHECK-NEXT: <array> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>17</integer> +// CHECK-NEXT: <key>col</key><integer>16</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>17</integer> +// CHECK-NEXT: <key>col</key><integer>16</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>depth</key><integer>0</integer> +// CHECK-NEXT: <key>extended_message</key> +// CHECK-NEXT: <string>Nullable pointer is passed to a callee that requires a non-null 1st parameter</string> +// CHECK-NEXT: <key>message</key> +// CHECK-NEXT: <string>Nullable pointer is passed to a callee that requires a non-null 1st parameter</string> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> +// CHECK-NEXT: <key>description</key><string>Nullable pointer is passed to a callee that requires a non-null 1st parameter</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> +// CHECK-NEXT: <key>type</key><string>Nullability</string> +// CHECK-NEXT: <key>check_name</key><string>nullability.NullPassedToNonnull</string> +// CHECK-NEXT: <!-- This hash is experimental and going to change! --> +// CHECK-NEXT: <key>issue_hash_content_of_line_in_context</key><string>b6bc8126de8e6eb3375483a656fe858d</string> +// CHECK-NEXT: <key>issue_context_kind</key><string>Objective-C method</string> +// CHECK-NEXT: <key>issue_context</key><string>method</string> +// CHECK-NEXT: <key>issue_hash_function_offset</key><string>3</string> +// CHECK-NEXT: <key>location</key> +// CHECK-NEXT: <dict> +// CHECK-NEXT: <key>line</key><integer>17</integer> +// CHECK-NEXT: <key>col</key><integer>3</integer> +// CHECK-NEXT: <key>file</key><integer>0</integer> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </dict> +// CHECK-NEXT: </array> diff --git a/test/Analysis/nullptr.cpp b/test/Analysis/nullptr.cpp index 229ad7bbb5..b3e61c9def 100644 --- a/test/Analysis/nullptr.cpp +++ b/test/Analysis/nullptr.cpp @@ -1,11 +1,12 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -Wno-conversion-null -analyzer-checker=core,debug.ExprInspection -analyzer-store region -verify %s +// RUN: %clang_analyze_cc1 -std=c++11 -Wno-conversion-null -analyzer-checker=core,debug.ExprInspection -analyzer-store region -analyzer-output=text -verify %s void clang_analyzer_eval(int); // test to see if nullptr is detected as a null pointer void foo1(void) { - char *np = nullptr; + char *np = nullptr; // expected-note{{'np' initialized to a null pointer value}} *np = 0; // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} } // check if comparing nullptr to nullptr is detected properly @@ -23,10 +24,11 @@ void foo3(void) { struct foo { int a, f; }; - char *np = nullptr; + char *np = nullptr; // expected-note{{'np' initialized to a null pointer value}} // casting a nullptr to anything should be caught eventually - int *ip = &(((struct foo *)np)->f); + int *ip = &(((struct foo *)np)->f); // expected-note{{'ip' initialized to a null pointer value}} *ip = 0; // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} // should be error here too, but analysis gets stopped // *np = 0; } @@ -49,16 +51,31 @@ int pr10372(void *& x) { } void zoo1() { - char **p = 0; + char **p = 0; // expected-note{{'p' initialized to a null pointer value}} delete *(p + 0); // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} +} + +void zoo1backwards() { + char **p = 0; // expected-note{{'p' initialized to a null pointer value}} + delete *(0 + p); // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} +} + +typedef __INTPTR_TYPE__ intptr_t; +void zoo1multiply() { + char **p = 0; // FIXME-should-be-note:{{'p' initialized to a null pointer value}} + delete *((char **)((intptr_t)p * 2)); // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} } void zoo2() { int **a = 0; - int **b = 0; + int **b = 0; // expected-note{{'b' initialized to a null pointer value}} asm ("nop" :"=r"(*a) :"0"(*b) // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} ); } @@ -70,17 +87,19 @@ int exprWithCleanups() { int a; }; - int *x = 0; + int *x = 0; // expected-note{{'x' initialized to a null pointer value}} return S(*x).a; // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} } int materializeTempExpr() { - int *n = 0; + int *n = 0; // expected-note{{'n' initialized to a null pointer value}} struct S { int a; S(int i): a(i) {} }; const S &s = S(*n); // expected-warning{{Dereference of null pointer}} + // expected-note@-1{{Dereference of null pointer}} return s.a; } @@ -98,6 +117,7 @@ struct X { void invokeF(X* x) { x->f(); // expected-warning{{Called C++ object pointer is null}} + // expected-note@-1{{Called C++ object pointer is null}} } struct Type { @@ -105,26 +125,41 @@ struct Type { }; void shouldNotCrash() { - decltype(nullptr) p; - if (getSymbol()) - invokeF(p); // expected-warning{{1st function call argument is an uninit}} - if (getSymbol()) - invokeF(nullptr); - if (getSymbol()) { - X *x = Type().x; + decltype(nullptr) p; // expected-note{{'p' declared without an initial value}} + if (getSymbol()) // expected-note {{Assuming the condition is false}} + // expected-note@-1{{Taking false branch}} + // expected-note@-2{{Assuming the condition is false}} + // expected-note@-3{{Taking false branch}} + // expected-note@-4{{Assuming the condition is true}} + // expected-note@-5{{Taking true branch}} + invokeF(p); // expected-warning{{1st function call argument is an uninitialized value}} + // expected-note@-1{{1st function call argument is an uninitialized value}} + if (getSymbol()) // expected-note {{Assuming the condition is false}} + // expected-note@-1{{Taking false branch}} + // expected-note@-2{{Assuming the condition is true}} + // expected-note@-3{{Taking true branch}} + invokeF(nullptr); // expected-note {{Calling 'invokeF'}} + // expected-note@-1{{Passing null pointer value via 1st parameter 'x'}} + if (getSymbol()) { // expected-note {{Assuming the condition is true}} + // expected-note@-1{{Taking true branch}} + X *x = Type().x; // expected-note{{'x' initialized to a null pointer value}} x->f(); // expected-warning{{Called C++ object pointer is null}} + // expected-note@-1{{Called C++ object pointer is null}} } } void f(decltype(nullptr) p) { int *q = nullptr; clang_analyzer_eval(p == 0); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} clang_analyzer_eval(q == 0); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} } decltype(nullptr) returnsNullPtrType(); void fromReturnType() { ((X *)returnsNullPtrType())->f(); // expected-warning{{Called C++ object pointer is null}} + // expected-note@-1{{Called C++ object pointer is null}} } #define AS_ATTRIBUTE __attribute__((address_space(256))) diff --git a/test/Analysis/objc-boxing.m b/test/Analysis/objc-boxing.m index 963374b3ef..66f24ddf77 100644 --- a/test/Analysis/objc-boxing.m +++ b/test/Analysis/objc-boxing.m @@ -5,6 +5,16 @@ void clang_analyzer_eval(int); typedef signed char BOOL; typedef long NSInteger; typedef unsigned long NSUInteger; + +@protocol NSObject +@end +@interface NSObject <NSObject> {} +@end +@protocol NSCopying +@end +@protocol NSCoding +@end + @interface NSString @end @interface NSString (NSStringExtensionMethods) + (id)stringWithUTF8String:(const char *)nullTerminatedCString; @@ -28,7 +38,15 @@ typedef unsigned long NSUInteger; + (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value ; @end +@interface NSValue : NSObject <NSCopying, NSCoding> +- (void)getValue:(void *)value; ++ (NSValue *)valueWithBytes:(const void *)value + objCType:(const char *)type; +@end +typedef typeof(sizeof(int)) size_t; +extern void *malloc(size_t); +extern void free(void *); extern char *strdup(const char *str); id constant_string() { @@ -39,6 +57,23 @@ id dynamic_string() { return @(strdup("boxed dynamic string")); // expected-warning{{Potential memory leak}} } +typedef struct __attribute__((objc_boxable)) { + const char *str; +} BoxableStruct; + +id leak_within_boxed_struct() { + BoxableStruct bs; + bs.str = strdup("dynamic string"); // The duped string shall be owned by val. + NSValue *val = @(bs); // no-warning + return val; +} + +id leak_of_boxed_struct() { + BoxableStruct *bs = malloc(sizeof(BoxableStruct)); // The pointer stored in bs isn't owned by val. + NSValue *val = @(*bs); // expected-warning{{Potential leak of memory pointed to by 'bs'}} + return val; +} + id const_char_pointer(int *x) { if (x) return @(3); diff --git a/test/Analysis/objc-encode.m b/test/Analysis/objc-encode.m new file mode 100644 index 0000000000..b2379e96d9 --- /dev/null +++ b/test/Analysis/objc-encode.m @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=debug.ExprInspection -verify %s +// expected-no-diagnostics + +void clang_analyzer_eval(int); + +// rdar://problem/34831581: Used to crash. +void foo(void) { + char buf1[] = @encode(int **); +} diff --git a/test/Analysis/objc-for.m b/test/Analysis/objc-for.m index 41709bee35..f1d0cf18d6 100644 --- a/test/Analysis/objc-for.m +++ b/test/Analysis/objc-for.m @@ -1,6 +1,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.cocoa.Loops,debug.ExprInspection -verify %s void clang_analyzer_eval(int); +void clang_analyzer_warnIfReached(); #define nil ((id)0) @@ -20,11 +21,13 @@ typedef unsigned long NSUInteger; @interface NSArray : NSObject <NSFastEnumeration> - (NSUInteger)count; - (NSEnumerator *)objectEnumerator; ++ (NSArray *)arrayWithObjects:(const id [])objects count:(NSUInteger)count; @end @interface NSDictionary : NSObject <NSFastEnumeration> - (NSUInteger)count; - (id)objectForKey:(id)key; ++ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id /* <NSCopying> */ [])keys count:(NSUInteger)count; @end @interface NSDictionary (SomeCategory) @@ -324,3 +327,19 @@ void protocolMethods(NSMutableArray *array) { for (id key in array) clang_analyzer_eval(0); // expected-warning{{FALSE}} } + +NSArray *globalArray; +NSDictionary *globalDictionary; +void boxedArrayEscape(NSMutableArray *array) { + if ([array count]) + return; + globalArray = @[array]; + for (id key in array) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} + + if ([array count]) + return; + globalDictionary = @{ @"array" : array }; + for (id key in array) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} +} diff --git a/test/Analysis/plist-macros.cpp b/test/Analysis/plist-macros.cpp index 594cfdc6ef..18d3ce11e6 100644 --- a/test/Analysis/plist-macros.cpp +++ b/test/Analysis/plist-macros.cpp @@ -218,7 +218,7 @@ void test2(int *p) { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Memory allocated by malloc() should be deallocated by free(), not 'delete'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Bad deallocator</string> // CHECK-NEXT: <key>check_name</key><string>unix.MismatchedDeallocator</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> @@ -315,7 +315,7 @@ void test2(int *p) { // CHECK-NEXT: </dict> // CHECK-NEXT: </array> // CHECK-NEXT: <key>description</key><string>Potential leak of memory pointed to by 'x'</string> -// CHECK-NEXT: <key>category</key><string>Memory Error</string> +// CHECK-NEXT: <key>category</key><string>Memory error</string> // CHECK-NEXT: <key>type</key><string>Memory leak</string> // CHECK-NEXT: <key>check_name</key><string>unix.Malloc</string> // CHECK-NEXT: <!-- This hash is experimental and going to change! --> diff --git a/test/Analysis/pointer-arithmetic.c b/test/Analysis/pointer-arithmetic.c new file mode 100644 index 0000000000..575dfffc01 --- /dev/null +++ b/test/Analysis/pointer-arithmetic.c @@ -0,0 +1,30 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s + +int test1() { + int *p = (int *)sizeof(int); + p -= 1; + return *p; // expected-warning {{Dereference of null pointer}} +} + +int test2() { + int *p = (int *)sizeof(int); + p -= 2; + p += 1; + return *p; // expected-warning {{Dereference of null pointer}} +} + +int test3() { + int *p = (int *)sizeof(int); + p++; + p--; + p--; + return *p; // expected-warning {{Dereference of null pointer}} +} + +int test4() { + // This is a special case where pointer arithmetic is not calculated to + // preserve useful warnings on dereferences of null pointers. + int *p = 0; + p += 1; + return *p; // expected-warning {{Dereference of null pointer}} +} diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index 542a339cd6..e792bb2e6b 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -987,5 +987,21 @@ void testOpaqueConsistency(OpaqueIntWrapper *w) { } @end + +@interface Wrapper +@property(nonatomic, readonly) int value; +@end + +@implementation Wrapper +@synthesize value; +@end + +void testNoCrashWhenAccessPropertyAndThereAreNoDirectBindingsAtAll() { + union { + Wrapper *wrapper; + } u = { 0 }; + [u.wrapper value]; +} + #endif // non-ARC diff --git a/test/Analysis/pthreadlock.c b/test/Analysis/pthreadlock.c index 9886817252..56a92d7d4d 100644 --- a/test/Analysis/pthreadlock.c +++ b/test/Analysis/pthreadlock.c @@ -176,6 +176,42 @@ ok22(void) { pthread_mutex_unlock(pmtx); // no-warning } +void ok23(void) { + if (pthread_mutex_destroy(&mtx1) != 0) // no-warning + pthread_mutex_destroy(&mtx1); // no-warning +} + +void ok24(void) { + if (pthread_mutex_destroy(&mtx1) != 0) // no-warning + pthread_mutex_lock(&mtx1); // no-warning +} + +void ok25(void) { + if (pthread_mutex_destroy(&mtx1) != 0) // no-warning + pthread_mutex_unlock(&mtx1); // no-warning +} + +void ok26(void) { + pthread_mutex_unlock(&mtx1); // no-warning + if (pthread_mutex_destroy(&mtx1) != 0) // no-warning + pthread_mutex_lock(&mtx1); // no-warning +} + +void ok27(void) { + pthread_mutex_unlock(&mtx1); // no-warning + if (pthread_mutex_destroy(&mtx1) != 0) // no-warning + pthread_mutex_lock(&mtx1); // no-warning + else + pthread_mutex_init(&mtx1, NULL); // no-warning +} + +void ok28(void) { + if (pthread_mutex_destroy(&mtx1) != 0) { // no-warning + pthread_mutex_lock(&mtx1); // no-warning + pthread_mutex_unlock(&mtx1); // no-warning + pthread_mutex_destroy(&mtx1); // no-warning + } +} void bad1(void) @@ -392,3 +428,46 @@ bad26(void) pthread_mutex_unlock(&mtx1); // no-warning pthread_mutex_init(&mtx1, NULL); // expected-warning{{This lock has already been initialized}} } + +void bad27(void) { + pthread_mutex_unlock(&mtx1); // no-warning + int ret = pthread_mutex_destroy(&mtx1); // no-warning + if (ret != 0) // no-warning + pthread_mutex_lock(&mtx1); // no-warning + else + pthread_mutex_unlock(&mtx1); // expected-warning{{This lock has already been destroyed}} +} + +void bad28(void) { + pthread_mutex_unlock(&mtx1); // no-warning + int ret = pthread_mutex_destroy(&mtx1); // no-warning + if (ret != 0) // no-warning + pthread_mutex_lock(&mtx1); // no-warning + else + pthread_mutex_lock(&mtx1); // expected-warning{{This lock has already been destroyed}} +} + +void bad29(void) { + pthread_mutex_lock(&mtx1); // no-warning + pthread_mutex_unlock(&mtx1); // no-warning + if (pthread_mutex_destroy(&mtx1) != 0) // no-warning + pthread_mutex_init(&mtx1, NULL); // expected-warning{{This lock has already been initialized}} + else + pthread_mutex_init(&mtx1, NULL); // no-warning +} + +void bad30(void) { + pthread_mutex_lock(&mtx1); // no-warning + pthread_mutex_unlock(&mtx1); // no-warning + if (pthread_mutex_destroy(&mtx1) != 0) // no-warning + pthread_mutex_init(&mtx1, NULL); // expected-warning{{This lock has already been initialized}} + else + pthread_mutex_destroy(&mtx1); // expected-warning{{This lock has already been destroyed}} +} + +void bad31(void) { + int ret = pthread_mutex_destroy(&mtx1); // no-warning + pthread_mutex_lock(&mtx1); // expected-warning{{This lock has already been destroyed}} + if (ret != 0) + pthread_mutex_lock(&mtx1); +} diff --git a/test/Analysis/ptr-arith.c b/test/Analysis/ptr-arith.c index b78ec503a1..93cb4ee9a6 100644 --- a/test/Analysis/ptr-arith.c +++ b/test/Analysis/ptr-arith.c @@ -342,3 +342,8 @@ void negativeIndex(char *str) { clang_analyzer_eval(*ptr3 == 'a'); // expected-warning{{UNKNOWN}} } +void test_no_crash_on_pointer_to_label() { + char *a = &&label; + a[0] = 0; +label:; +} diff --git a/test/Analysis/ptr-arith.cpp b/test/Analysis/ptr-arith.cpp index e1860f4268..1eec83c643 100644 --- a/test/Analysis/ptr-arith.cpp +++ b/test/Analysis/ptr-arith.cpp @@ -98,3 +98,22 @@ void checkMultiDimansionalArray() { int a[5][5]; *(*(a+1)+2) = 2; } + +unsigned ptrSubtractionNoCrash(char *Begin, char *End) { + auto N = End - Begin; + if (Begin) + return 0; + return N; +} + +// Bug 34309 +bool ptrAsIntegerSubtractionNoCrash(__UINTPTR_TYPE__ x, char *p) { + __UINTPTR_TYPE__ y = (__UINTPTR_TYPE__)p - 1; + return y == x; +} + +// Bug 34374 +bool integerAsPtrSubtractionNoCrash(char *p, __UINTPTR_TYPE__ m) { + auto n = p - reinterpret_cast<char*>((__UINTPTR_TYPE__)1); + return n == m; +} diff --git a/test/Analysis/reference.cpp b/test/Analysis/reference.cpp index b323b96661..c269dd7bad 100644 --- a/test/Analysis/reference.cpp +++ b/test/Analysis/reference.cpp @@ -117,6 +117,11 @@ void testRetroactiveNullReference(int *x) { y = 5; // expected-warning{{Dereference of null pointer}} } +namespace TestReferenceAddress { +struct S { int &x; }; +S getS(); +S *getSP(); + void testReferenceAddress(int &x) { // FIXME: Move non-zero reference assumption out of RangeConstraintManager.cpp:422 #ifdef ANALYZER_CM_Z3 @@ -127,23 +132,19 @@ void testReferenceAddress(int &x) { clang_analyzer_eval(&ref() != 0); // expected-warning{{TRUE}} #endif - struct S { int &x; }; - - extern S getS(); #ifdef ANALYZER_CM_Z3 clang_analyzer_eval(&getS().x != 0); // expected-warning{{UNKNOWN}} #else clang_analyzer_eval(&getS().x != 0); // expected-warning{{TRUE}} #endif - extern S *getSP(); #ifdef ANALYZER_CM_Z3 clang_analyzer_eval(&getSP()->x != 0); // expected-warning{{UNKNOWN}} #else clang_analyzer_eval(&getSP()->x != 0); // expected-warning{{TRUE}} #endif } - +} void testFunctionPointerReturn(void *opaque) { typedef int &(*RefFn)(); diff --git a/test/Analysis/retain-release-inline.m b/test/Analysis/retain-release-inline.m index 0cde2c1cf5..4fe6bca4a4 100644 --- a/test/Analysis/retain-release-inline.m +++ b/test/Analysis/retain-release-inline.m @@ -12,7 +12,7 @@ // // It includes the basic definitions for the test cases below. //===----------------------------------------------------------------------===// - +#define NULL 0 typedef unsigned int __darwin_natural_t; typedef unsigned long uintptr_t; typedef unsigned int uint32_t; @@ -267,6 +267,10 @@ typedef NSUInteger NSStringEncoding; extern CFStringRef CFStringCreateWithCStringNoCopy(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding, CFAllocatorRef contentsDeallocator); +typedef struct { + int ref; +} isl_basic_map; + //===----------------------------------------------------------------------===// // Test cases. //===----------------------------------------------------------------------===// @@ -285,6 +289,7 @@ void test() { foo(s); bar(s); } + void test_neg() { NSString *s = [[NSString alloc] init]; // no-warning foo(s); @@ -294,6 +299,89 @@ void test_neg() { bar(s); } +__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *isl_basic_map_cow(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap); +void free(void *); + +void callee_side_parameter_checking_leak(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap) { // expected-warning {{Potential leak of an object}} +} + +// As 'isl_basic_map_free' is annotated with 'rc_ownership_trusted_implementation', RetainCountChecker trusts its +// implementation and doesn't analyze its body. If the annotation 'rc_ownership_trusted_implementation' is removed, +// a leak warning is raised by RetainCountChecker as the analyzer is unable to detect a decrement in the reference +// count of 'bmap' along the path in 'isl_basic_map_free' assuming the predicate of the second 'if' branch to be +// true or assuming both the predicates in the function to be false. +__attribute__((annotate("rc_ownership_trusted_implementation"))) isl_basic_map *isl_basic_map_free(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap) { + if (!bmap) + return NULL; + + if (--bmap->ref > 0) + return NULL; + + free(bmap); + return NULL; +} + +// As 'isl_basic_map_copy' is annotated with 'rc_ownership_trusted_implementation', RetainCountChecker trusts its +// implementation and doesn't analyze its body. If that annotation is removed, a 'use-after-release' warning might +// be raised by RetainCountChecker as the pointer which is passed as an argument to this function and the pointer +// which is returned from the function point to the same memory location. +__attribute__((annotate("rc_ownership_trusted_implementation"))) __attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *isl_basic_map_copy(isl_basic_map *bmap) { + if (!bmap) + return NULL; + + bmap->ref++; + return bmap; +} + +void test_use_after_release_with_trusted_implementation_annotate_attribute(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap) { + // After this call, 'bmap' has a +1 reference count. + bmap = isl_basic_map_cow(bmap); + // After the call to 'isl_basic_map_copy', 'bmap' has a +1 reference count. + isl_basic_map *temp = isl_basic_map_cow(isl_basic_map_copy(bmap)); + // After this call, 'bmap' has a +0 reference count. + isl_basic_map *temp2 = isl_basic_map_cow(bmap); // no-warning + isl_basic_map_free(temp2); + isl_basic_map_free(temp); +} + +void test_leak_with_trusted_implementation_annotate_attribute(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap) { + // After this call, 'bmap' has a +1 reference count. + bmap = isl_basic_map_cow(bmap); // no-warning + // After this call, 'bmap' has a +0 reference count. + isl_basic_map_free(bmap); +} + +void callee_side_parameter_checking_incorrect_rc_decrement(isl_basic_map *bmap) { + isl_basic_map_free(bmap); // expected-warning {{Incorrect decrement of the reference count}} +} + +__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *callee_side_parameter_checking_return_notowned_object(isl_basic_map *bmap) { + return bmap; // expected-warning {{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} +} + +__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *callee_side_parameter_checking_assign_consumed_parameter_leak_return(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap1, __attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap2) { // expected-warning {{Potential leak of an object}} + bmap1 = bmap2; + isl_basic_map_free(bmap2); + return bmap1; +} + +__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *callee_side_parameter_checking_assign_consumed_parameter_leak(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap1, __attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap2) { // expected-warning {{Potential leak of an object}} + bmap1 = bmap2; + isl_basic_map_free(bmap1); + return bmap2; +} + +__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *error_path_leak(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap1, __attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap2) { // expected-warning {{Potential leak of an object}} + bmap1 = isl_basic_map_cow(bmap1); + if (!bmap1 || !bmap2) + goto error; + + isl_basic_map_free(bmap2); + return bmap1; +error: + return isl_basic_map_free(bmap1); +} + //===----------------------------------------------------------------------===// // Test returning retained and not-retained values. //===----------------------------------------------------------------------===// diff --git a/test/Analysis/retain-release-safe.c b/test/Analysis/retain-release-safe.c new file mode 100644 index 0000000000..de74355242 --- /dev/null +++ b/test/Analysis/retain-release-safe.c @@ -0,0 +1,72 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.RetainCount -verify %s + +#pragma clang arc_cf_code_audited begin +typedef const void * CFTypeRef; +extern CFTypeRef CFRetain(CFTypeRef cf); +extern void CFRelease(CFTypeRef cf); +#pragma clang arc_cf_code_audited end + +#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) +#define CF_CONSUMED __attribute__((cf_consumed)) + +extern CFTypeRef CFCreate() CF_RETURNS_RETAINED; + +// A "safe" variant of CFRetain that doesn't crash when a null pointer is +// retained. This is often defined by users in a similar manner. The +// CF_RETURNS_RETAINED annotation is misleading here, because the function +// is not supposed to return an object with a +1 retain count. Instead, it +// is supposed to return an object with +(N+1) retain count, where N is +// the original retain count of 'cf'. However, there is no good annotation +// to use in this case, and it is pointless to provide such annotation +// because the only use cases would be CFRetain and SafeCFRetain. +// So instead we teach the analyzer to be able to accept such code +// and ignore the misplaced annotation. +CFTypeRef SafeCFRetain(CFTypeRef cf) CF_RETURNS_RETAINED { + if (cf) { + return CFRetain(cf); + } + return cf; +} + +// A "safe" variant of CFRelease that doesn't crash when a null pointer is +// released. The CF_CONSUMED annotation seems reasonable here. +void SafeCFRelease(CFTypeRef CF_CONSUMED cf) { + if (cf) + CFRelease(cf); // no-warning (when inlined) +} + +void escape(CFTypeRef cf); + +void makeSureTestsWork() { + CFTypeRef cf = CFCreate(); + CFRelease(cf); + CFRelease(cf); // expected-warning{{Reference-counted object is used after it is released}} +} + +// Make sure we understand that the second SafeCFRetain doesn't return an +// object with +1 retain count, which we won't be able to release twice. +void falseOverrelease(CFTypeRef cf) { + SafeCFRetain(cf); + SafeCFRetain(cf); + SafeCFRelease(cf); + SafeCFRelease(cf); // no-warning after inlining this. +} + +// Regular CFRelease() should behave similarly. +void sameWithNormalRelease(CFTypeRef cf) { + SafeCFRetain(cf); + SafeCFRetain(cf); + CFRelease(cf); + CFRelease(cf); // no-warning +} + +// Make sure we understand that the second SafeCFRetain doesn't return an +// object with +1 retain count, which would no longer be owned by us after +// it escapes to escape() and released once. +void falseReleaseNotOwned(CFTypeRef cf) { + SafeCFRetain(cf); + SafeCFRetain(cf); + escape(cf); + SafeCFRelease(cf); + SafeCFRelease(cf); // no-warning after inlining this. +} diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m index 39a3baa896..a1cc62cb35 100644 --- a/test/Analysis/retain-release.m +++ b/test/Analysis/retain-release.m @@ -325,6 +325,9 @@ typedef const struct __CFUUID * CFUUIDRef; extern void *CFPlugInInstanceCreate(CFAllocatorRef allocator, CFUUIDRef factoryUUID, CFUUIDRef typeUUID); +typedef struct { + int ref; +} isl_basic_map; //===----------------------------------------------------------------------===// // Test cases. @@ -574,6 +577,14 @@ void f17(int x, CFTypeRef p) { } } +__attribute__((annotate("rc_ownership_returns_retained"))) isl_basic_map *isl_basic_map_cow(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap); + +// Test custom diagnostics for generalized objects. +void f18(__attribute__((annotate("rc_ownership_consumed"))) isl_basic_map *bmap) { + // After this call, 'bmap' has a +1 reference count. + bmap = isl_basic_map_cow(bmap); // expected-warning {{Potential leak of an object}} +} + // Test basic tracking of ivars associated with 'self'. For the retain/release // checker we currently do not want to flag leaks associated with stores // of tracked objects to ivars. @@ -1447,7 +1458,7 @@ typedef NSString* MyStringTy; + (void) consume2:(id) CF_CONSUMED x; @end -static int ownership_attribute_doesnt_go_here NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' attribute only applies to functions and methods}} +static int ownership_attribute_doesnt_go_here NS_RETURNS_RETAINED; // expected-warning{{'ns_returns_retained' only applies to function types; type here is 'int'}} void test_attr_1(TestOwnershipAttr *X) { NSString *str = [X returnsAnOwnedString]; // expected-warning{{leak}} @@ -1776,15 +1787,15 @@ CFArrayRef camel_copymachine() { // rdar://problem/8024350 @protocol F18P -- (id) clone; +- (id) clone; // expected-note 2 {{method declared here}} @end @interface F18 : NSObject<F18P> @end @interface F18(Cat) -- (id) clone NS_RETURNS_RETAINED; +- (id) clone NS_RETURNS_RETAINED; // expected-warning {{overriding method has mismatched ns_returns_retained attributes}} @end @implementation F18 -- (id) clone { +- (id) clone { // expected-warning {{overriding method has mismatched ns_returns_retained attributes}} return [F18 alloc]; } @end diff --git a/test/Analysis/retain-release.mm b/test/Analysis/retain-release.mm index c9817005c2..ac83c1a48e 100644 --- a/test/Analysis/retain-release.mm +++ b/test/Analysis/retain-release.mm @@ -461,3 +461,12 @@ void radar13722286::PrepareBitmap() { } } +// rdar://34210609 +void _() { _(); }; // no-warning + +// Do not assume that IOBSDNameMatching increments a reference counter, +// unless return type is CFMutableDictionaryRef. +void* IOBSDNameMatching(); +void rdar33832412() { + void* x = IOBSDNameMatching(); // no-warning +} diff --git a/test/Analysis/simple-stream-checks.c b/test/Analysis/simple-stream-checks.c index f47e488110..ca1c781575 100644 --- a/test/Analysis/simple-stream-checks.c +++ b/test/Analysis/simple-stream-checks.c @@ -65,7 +65,7 @@ void SymbolEscapedThroughFunctionCall() { } FILE *GlobalF; -void SymbolEscapedThroughAssignmentToGloabl() { +void SymbolEscapedThroughAssignmentToGlobal() { FILE *F = fopen("myfile.txt", "w"); GlobalF = F; return; // no warning diff --git a/test/Analysis/taint-generic.c b/test/Analysis/taint-generic.c index 8efed66dac..d3ca246b82 100644 --- a/test/Analysis/taint-generic.c +++ b/test/Analysis/taint-generic.c @@ -192,20 +192,41 @@ void testStruct() { void testStructArray() { struct { - char buf[16]; - struct { - int length; - } st[1]; - } tainted; + int length; + } tainted[4]; - char buffer[16]; + char dstbuf[16], srcbuf[16]; int sock; sock = socket(AF_INET, SOCK_STREAM, 0); - read(sock, &tainted.buf[0], sizeof(tainted.buf)); - read(sock, &tainted.st[0], sizeof(tainted.st)); - // FIXME: tainted.st[0].length should be marked tainted - __builtin_memcpy(buffer, tainted.buf, tainted.st[0].length); // no-warning + __builtin_memset(srcbuf, 0, sizeof(srcbuf)); + + read(sock, &tainted[0], sizeof(tainted)); + __builtin_memcpy(dstbuf, srcbuf, tainted[0].length); // expected-warning {{Untrusted data is used to specify the buffer size}} + + __builtin_memset(&tainted, 0, sizeof(tainted)); + read(sock, &tainted, sizeof(tainted)); + __builtin_memcpy(dstbuf, srcbuf, tainted[0].length); // expected-warning {{Untrusted data is used to specify the buffer size}} + + __builtin_memset(&tainted, 0, sizeof(tainted)); + // If we taint element 1, we should not raise an alert on taint for element 0 or element 2 + read(sock, &tainted[1], sizeof(tainted)); + __builtin_memcpy(dstbuf, srcbuf, tainted[0].length); // no-warning + __builtin_memcpy(dstbuf, srcbuf, tainted[2].length); // no-warning +} + +void testUnion() { + union { + int x; + char y[4]; + } tainted; + + char buffer[4]; + + int sock = socket(AF_INET, SOCK_STREAM, 0); + read(sock, &tainted.y, sizeof(tainted.y)); + // FIXME: overlapping regions aren't detected by isTainted yet + __builtin_memcpy(buffer, tainted.y, tainted.x); } int testDivByZero() { diff --git a/test/Analysis/temp-obj-dtors-cfg-output.cpp b/test/Analysis/temp-obj-dtors-cfg-output.cpp index 372443bedf..05b6a31059 100644 --- a/test/Analysis/temp-obj-dtors-cfg-output.cpp +++ b/test/Analysis/temp-obj-dtors-cfg-output.cpp @@ -834,7 +834,8 @@ int testConsistencyNestedNormalReturn(bool value) { // CHECK: Preds (1): B4 // CHECK: Succs (1): B1 // CHECK: [B4] -// CHECK: 1: [B7.2] ?: [B6.6] +// CXX98: 1: [B7.2] ?: [B6.6] +// CXX11: 1: [B7.3] ?: [B6.6] // CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A) // CHECK: 3: [B4.2] // CHECK: 4: [B4.3] (CXXConstructExpr, class A) @@ -843,10 +844,13 @@ int testConsistencyNestedNormalReturn(bool value) { // CHECK: Preds (2): B5 B6 // CHECK: Succs (2): B3 B2 // CHECK: [B5] -// CHECK: 1: [B7.2] (ImplicitCastExpr, NoOp, const class A) -// CHECK: 2: [B5.1] -// CHECK: 3: [B5.2] (CXXConstructExpr, class A) -// CHECK: 4: [B5.3] (BindTemporary) +// CXX98: 1: [B7.2] (ImplicitCastExpr, NoOp, const class A) +// CXX98: 2: [B5.1] +// CXX98: 3: [B5.2] (CXXConstructExpr, class A) +// CXX98: 4: [B5.3] (BindTemporary) +// CXX11: 1: [B7.3] (ImplicitCastExpr, NoOp, const class A) +// CXX11: 2: [B5.1] (CXXConstructExpr, class A) +// CXX11: 3: [B5.2] (BindTemporary) // CHECK: Preds (1): B7 // CHECK: Succs (1): B4 // CHECK: [B6] @@ -861,10 +865,15 @@ int testConsistencyNestedNormalReturn(bool value) { // CHECK: [B7] // CHECK: 1: A() (CXXConstructExpr, class A) // CHECK: 2: [B7.1] (BindTemporary) -// CHECK: 3: [B7.2].operator bool -// CHECK: 4: [B7.2] -// CHECK: 5: [B7.4] (ImplicitCastExpr, UserDefinedConversion, _Bool) -// CHECK: T: [B7.5] ? ... : ... +// CXX98: 3: [B7.2].operator bool +// CXX98: 4: [B7.2] +// CXX98: 5: [B7.4] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CXX98: T: [B7.5] ? ... : ... +// CXX11: 3: [B7.2] +// CXX11: 4: [B7.3].operator bool +// CXX11: 5: [B7.3] +// CXX11: 6: [B7.5] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CXX11: T: [B7.6] ? ... : ... // CHECK: Preds (1): B8 // CHECK: Succs (2): B5 B6 // CHECK: [B0 (EXIT)] @@ -886,7 +895,8 @@ int testConsistencyNestedNormalReturn(bool value) { // CHECK: Preds (1): B4 // CHECK: Succs (1): B1 // CHECK: [B4] -// CHECK: 1: [B7.4] ?: [B6.6] +// CXX98: 1: [B7.4] ?: [B6.6] +// CXX11: 1: [B7.5] ?: [B6.6] // CHECK: 2: [B4.1] (ImplicitCastExpr, NoOp, const class A) // CHECK: 3: [B4.2] // CHECK: 4: [B7.2]([B4.3]) @@ -894,10 +904,13 @@ int testConsistencyNestedNormalReturn(bool value) { // CHECK: Preds (2): B5 B6 // CHECK: Succs (2): B3 B2 // CHECK: [B5] -// CHECK: 1: [B7.4] (ImplicitCastExpr, NoOp, const class A) -// CHECK: 2: [B5.1] -// CHECK: 3: [B5.2] (CXXConstructExpr, class A) -// CHECK: 4: [B5.3] (BindTemporary) +// CXX98: 1: [B7.4] (ImplicitCastExpr, NoOp, const class A) +// CXX98: 2: [B5.1] +// CXX98: 3: [B5.2] (CXXConstructExpr, class A) +// CXX98: 4: [B5.3] (BindTemporary) +// CXX11: 1: [B7.5] (ImplicitCastExpr, NoOp, const class A) +// CXX11: 2: [B5.1] (CXXConstructExpr, class A) +// CXX11: 3: [B5.2] (BindTemporary) // CHECK: Preds (1): B7 // CHECK: Succs (1): B4 // CHECK: [B6] @@ -914,10 +927,15 @@ int testConsistencyNestedNormalReturn(bool value) { // CHECK: 2: [B7.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &)) // CHECK: 3: A() (CXXConstructExpr, class A) // CHECK: 4: [B7.3] (BindTemporary) -// CHECK: 5: [B7.4].operator bool -// CHECK: 6: [B7.4] -// CHECK: 7: [B7.6] (ImplicitCastExpr, UserDefinedConversion, _Bool) -// CHECK: T: [B7.7] ? ... : ... +// CXX98: 5: [B7.4].operator bool +// CXX98: 6: [B7.4] +// CXX98: 7: [B7.6] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CXX98: T: [B7.7] ? ... : ... +// CXX11: 5: [B7.4] +// CXX11: 6: [B7.5].operator bool +// CXX11: 7: [B7.5] +// CXX11: 8: [B7.7] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CXX11: T: [B7.8] ? ... : ... // CHECK: Preds (2): B8 B9 // CHECK: Succs (2): B5 B6 // CHECK: [B8] @@ -925,7 +943,8 @@ int testConsistencyNestedNormalReturn(bool value) { // CHECK: Preds (1): B9 // CHECK: Succs (1): B7 // CHECK: [B9] -// CHECK: 1: [B12.2] ?: [B11.6] +// CXX98: 1: [B12.2] ?: [B11.6] +// CXX11: 1: [B12.3] ?: [B11.6] // CHECK: 2: [B9.1] (ImplicitCastExpr, NoOp, const class A) // CHECK: 3: [B9.2] // CHECK: 4: const A &a = A() ?: A(); @@ -933,10 +952,13 @@ int testConsistencyNestedNormalReturn(bool value) { // CHECK: Preds (2): B10 B11 // CHECK: Succs (2): B8 B7 // CHECK: [B10] -// CHECK: 1: [B12.2] (ImplicitCastExpr, NoOp, const class A) -// CHECK: 2: [B10.1] -// CHECK: 3: [B10.2] (CXXConstructExpr, class A) -// CHECK: 4: [B10.3] (BindTemporary) +// CXX98: 1: [B12.2] (ImplicitCastExpr, NoOp, const class A) +// CXX98: 2: [B10.1] +// CXX98: 3: [B10.2] (CXXConstructExpr, class A) +// CXX98: 4: [B10.3] (BindTemporary) +// CXX11: 1: [B12.3] (ImplicitCastExpr, NoOp, const class A) +// CXX11: 2: [B10.1] (CXXConstructExpr, class A) +// CXX11: 3: [B10.2] (BindTemporary) // CHECK: Preds (1): B12 // CHECK: Succs (1): B9 // CHECK: [B11] @@ -951,10 +973,15 @@ int testConsistencyNestedNormalReturn(bool value) { // CHECK: [B12] // CHECK: 1: A() (CXXConstructExpr, class A) // CHECK: 2: [B12.1] (BindTemporary) -// CHECK: 3: [B12.2].operator bool -// CHECK: 4: [B12.2] -// CHECK: 5: [B12.4] (ImplicitCastExpr, UserDefinedConversion, _Bool) -// CHECK: T: [B12.5] ? ... : ... +// CXX98: 3: [B12.2].operator bool +// CXX98: 4: [B12.2] +// CXX98: 5: [B12.4] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CXX98: T: [B12.5] ? ... : ... +// CXX11: 3: [B12.2] +// CXX11: 4: [B12.3].operator bool +// CXX11: 5: [B12.3] +// CXX11: 6: [B12.5] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CXX11: T: [B12.6] ? ... : ... // CHECK: Preds (1): B13 // CHECK: Succs (2): B10 B11 // CHECK: [B0 (EXIT)] diff --git a/test/Analysis/temporaries-callback-order.cpp b/test/Analysis/temporaries-callback-order.cpp new file mode 100644 index 0000000000..df916cc4e7 --- /dev/null +++ b/test/Analysis/temporaries-callback-order.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=debug.AnalysisOrder -analyzer-config debug.AnalysisOrder:Bind=true -analyzer-config debug.AnalysisOrder:RegionChanges=true %s 2>&1 | FileCheck %s + +struct Super { + virtual void m(); +}; +struct Sub : Super { + virtual void m() {} +}; + +void testTemporaries() { + // This triggers RegionChanges twice: + // - Once for zero-initialization of the structure. + // - Once for creating a temporary region and copying the structure there. + // FIXME: This code shouldn't really produce the extra temporary, however + // that's how we behave for now. + Sub().m(); +} + +void seeIfCheckBindWorks() { + // This should trigger checkBind. The rest of the code shouldn't. + // This also triggers checkRegionChanges after that. + // Note that this function is analyzed first, so the messages would be on top. + int x = 1; +} + +// seeIfCheckBindWorks(): +// CHECK: Bind +// CHECK-NEXT: RegionChanges + +// testTemporaries(): +// CHECK-NEXT: RegionChanges +// CHECK-NEXT: RegionChanges + +// Make sure there's no further output. +// CHECK-NOT: Bind +// CHECK-NOT: RegionChanges diff --git a/test/Analysis/temporaries.cpp b/test/Analysis/temporaries.cpp index 2c052f8b51..99851dd685 100644 --- a/test/Analysis/temporaries.cpp +++ b/test/Analysis/temporaries.cpp @@ -503,3 +503,30 @@ namespace PR32088 { }); } } + +namespace CopyToTemporaryCorrectly { +class Super { +public: + void m() { + mImpl(); + } + virtual void mImpl() = 0; +}; +class Sub : public Super { +public: + Sub(const int &p) : j(p) {} + virtual void mImpl() override { + // Used to be undefined pointer dereference because we didn't copy + // the subclass data (j) to the temporary object properly. + (void)(j + 1); // no-warning + if (j != 22) { + clang_analyzer_warnIfReached(); // no-warning + } + } + const int &j; +}; +void run() { + int i = 22; + Sub(i).m(); +} +} diff --git a/test/Analysis/uninit-const.cpp b/test/Analysis/uninit-const.cpp index 75e932a77c..db969bfb67 100644 --- a/test/Analysis/uninit-const.cpp +++ b/test/Analysis/uninit-const.cpp @@ -122,7 +122,7 @@ void f1(void) { } void f_uninit(void) { - int x; + int x; // expected-note {{'x' declared without an initial value}} doStuff_uninit(&x); // expected-warning {{1st function call argument is a pointer to uninitialized value}} // expected-note@-1 {{1st function call argument is a pointer to uninitialized value}} } diff --git a/test/Analysis/unix-fns.c b/test/Analysis/unix-fns.c index 3eccd51d2c..481f545e28 100644 --- a/test/Analysis/unix-fns.c +++ b/test/Analysis/unix-fns.c @@ -1,7 +1,7 @@ -// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,unix.API,osx.API %s -analyzer-store=region -analyzer-output=plist -analyzer-eagerly-assume -analyzer-config faux-bodies=true -analyzer-config path-diagnostics-alternate=false -fblocks -verify -o %t.plist +// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,unix.API,osx.API,optin.portability %s -analyzer-store=region -analyzer-output=plist -analyzer-eagerly-assume -analyzer-config faux-bodies=true -analyzer-config path-diagnostics-alternate=false -fblocks -verify -o %t.plist // RUN: FileCheck --input-file=%t.plist %s // RUN: mkdir -p %t.dir -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.API,osx.API -analyzer-output=html -analyzer-config faux-bodies=true -fblocks -o %t.dir %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.API,osx.API,optin.portability -analyzer-output=html -analyzer-config faux-bodies=true -fblocks -o %t.dir %s // RUN: rm -fR %t.dir struct _opaque_pthread_once_t { long __sig; diff --git a/test/Analysis/unreachable-code-path.c b/test/Analysis/unreachable-code-path.c index ff58587be2..effa4d9bfa 100644 --- a/test/Analysis/unreachable-code-path.c +++ b/test/Analysis/unreachable-code-path.c @@ -213,3 +213,13 @@ void macro(void) { RETURN(1); // no-warning } +// Avoid FP when macro argument is known +void writeSomething(int *x); +#define MACRO(C) \ + if (!C) { \ + static int x; \ + writeSomething(&x); \ + } +void macro2(void) { + MACRO(1); +} diff --git a/test/Analysis/unsupported-types.c b/test/Analysis/unsupported-types.c new file mode 100644 index 0000000000..9233095e42 --- /dev/null +++ b/test/Analysis/unsupported-types.c @@ -0,0 +1,31 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -triple x86_64-unknown-linux -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -triple powerpc64-linux-gnu -verify %s + +#define _Complex_I (__extension__ 1.0iF) + +void clang_analyzer_eval(int); + +void complex_float(double _Complex x, double _Complex y) { + clang_analyzer_eval(x == y); // expected-warning{{UNKNOWN}} + if (x != 1.0 + 3.0 * _Complex_I && y != 1.0 - 4.0 * _Complex_I) + return + clang_analyzer_eval(x == y); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x + y == 2.0 - 1.0 * _Complex_I); // expected-warning{{UNKNOWN}} +} + +void complex_int(int _Complex x, int _Complex y) { + clang_analyzer_eval(x == y); // expected-warning{{UNKNOWN}} + if (x != 1.0 + 3.0 * _Complex_I && y != 1.0 - 4.0 * _Complex_I) + return + clang_analyzer_eval(x == y); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x + y == 2.0 - 1.0 * _Complex_I); // expected-warning{{UNKNOWN}} +} + +void longdouble_float(long double x, long double y) { + clang_analyzer_eval(x == y); // expected-warning{{UNKNOWN}} + if (x != 0.0L && y != 1.0L) + return + clang_analyzer_eval(x == y); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x + y == 1.0L); // expected-warning{{UNKNOWN}} +} diff --git a/test/Analysis/virtualcall.cpp b/test/Analysis/virtualcall.cpp index 56bd32446e..c22a8463c0 100644 --- a/test/Analysis/virtualcall.cpp +++ b/test/Analysis/virtualcall.cpp @@ -1,98 +1,83 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -verify -std=c++11 %s -// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:Interprocedural=true -DINTERPROCEDURAL=1 -verify -std=c++11 %s -// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:PureOnly=true -DPUREONLY=1 -verify -std=c++11 %s +// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-output=text -verify -std=c++11 %s -/* When INTERPROCEDURAL is set, we expect diagnostics in all functions reachable - from a constructor or destructor. If it is not set, we expect diagnostics - only in the constructor or destructor. +// RUN: %clang_analyze_cc1 -analyzer-checker=optin.cplusplus.VirtualCall -analyzer-store region -analyzer-config optin.cplusplus.VirtualCall:PureOnly=true -DPUREONLY=1 -analyzer-output=text -verify -std=c++11 %s - When PUREONLY is set, we expect diagnostics only for calls to pure virtual - functions not to non-pure virtual functions. -*/ +#include "virtualcall.h" class A { public: A(); - A(int i); - ~A() {}; - - virtual int foo() = 0; // from Sema: expected-note {{'foo' declared here}} + ~A(){}; + + virtual int foo() = 0; virtual void bar() = 0; void f() { foo(); -#if INTERPROCEDURAL - // expected-warning-re@-2 {{{{^}}Call Path : foo <-- fCall to pure virtual function during construction has undefined behavior}} -#endif + // expected-warning-re@-1 {{{{^}}Call to pure virtual function during construction}} + // expected-note-re@-2 {{{{^}}Call to pure virtual function during construction}} } }; class B : public A { public: - B() { - foo(); + B() { // expected-note {{Calling default constructor for 'A'}} + foo(); #if !PUREONLY -#if INTERPROCEDURAL - // expected-warning-re@-3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}} -#else - // expected-warning-re@-5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}} -#endif + // expected-warning-re@-2 {{{{^}}Call to virtual function during construction}} + // expected-note-re@-3 {{{{^}}This constructor of an object of type 'B' has not returned when the virtual method was called}} + // expected-note-re@-4 {{{{^}}Call to virtual function during construction}} #endif - } ~B(); - + virtual int foo(); - virtual void bar() { foo(); } -#if INTERPROCEDURAL - // expected-warning-re@-2 {{{{^}}Call Path : foo <-- barCall to virtual function during destruction will not dispatch to derived class}} + virtual void bar() { + foo(); +#if !PUREONLY + // expected-warning-re@-2 {{{{^}}Call to virtual function during destruction}} + // expected-note-re@-3 {{{{^}}Call to virtual function during destruction}} #endif + } }; -A::A() { - f(); -} - -A::A(int i) { - foo(); // From Sema: expected-warning {{call to pure virtual member function 'foo' has undefined behavior}} -#if INTERPROCEDURAL - // expected-warning-re@-2 {{{{^}}Call Path : fooCall to pure virtual function during construction has undefined behavior}} -#else - // expected-warning-re@-4 {{{{^}}Call to pure virtual function during construction has undefined behavior}} -#endif +A::A() { + f(); +// expected-note-re@-1 {{{{^}}This constructor of an object of type 'A' has not returned when the virtual method was called}} +// expected-note-re@-2 {{{{^}}Calling 'A::f'}} } B::~B() { this->B::foo(); // no-warning this->B::bar(); - this->foo(); #if !PUREONLY -#if INTERPROCEDURAL - // expected-warning-re@-3 {{{{^}}Call Path : fooCall to virtual function during destruction will not dispatch to derived class}} -#else - // expected-warning-re@-5 {{{{^}}Call to virtual function during destruction will not dispatch to derived class}} + // expected-note-re@-2 {{{{^}}This destructor of an object of type '~B' has not returned when the virtual method was called}} + // expected-note-re@-3 {{{{^}}Calling 'B::bar'}} #endif + this->foo(); +#if !PUREONLY + // expected-warning-re@-2 {{{{^}}Call to virtual function during destruction}} + // expected-note-re@-3 {{{{^}}This destructor of an object of type '~B' has not returned when the virtual method was called}} + // expected-note-re@-4 {{{{^}}Call to virtual function during destruction}} #endif - + } class C : public B { public: C(); ~C(); - + virtual int foo(); void f(int i); }; C::C() { - f(foo()); + f(foo()); #if !PUREONLY -#if INTERPROCEDURAL - // expected-warning-re@-3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}} -#else - // expected-warning-re@-5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}} -#endif + // expected-warning-re@-2 {{{{^}}Call to virtual function during construction}} + // expected-note-re@-3 {{{{^}}This constructor of an object of type 'C' has not returned when the virtual method was called}} + // expected-note-re@-4 {{{{^}}Call to virtual function during construction}} #endif } @@ -112,30 +97,198 @@ public: foo(); // no-warning } ~E() { bar(); } +#if !PUREONLY + // expected-note-re@-2 2{{{{^}}Calling '~B'}} +#endif int foo() override; }; -// Regression test: don't crash when there's no direct callee. class F { public: F() { - void (F::* ptr)() = &F::foo; + void (F::*ptr)() = &F::foo; (this->*ptr)(); } void foo(); }; +class G { +public: + G() {} + virtual void bar(); + void foo() { + bar(); // no warning + } +}; + +class H { +public: + H() : initState(0) { init(); } + int initState; + virtual void f() const; + void init() { + if (initState) + f(); // no warning + } + + H(int i) { + G g; + g.foo(); + g.bar(); // no warning + f(); +#if !PUREONLY + // expected-warning-re@-2 {{{{^}}Call to virtual function during construction}} + // expected-note-re@-3 {{{{^}}This constructor of an object of type 'H' has not returned when the virtual method was called}} + // expected-note-re@-4 {{{{^}}Call to virtual function during construction}} +#endif + H &h = *this; + h.f(); +#if !PUREONLY + // expected-warning-re@-2 {{{{^}}Call to virtual function during construction}} + // expected-note-re@-3 {{{{^}}This constructor of an object of type 'H' has not returned when the virtual method was called}} + // expected-note-re@-4 {{{{^}}Call to virtual function during construction}} +#endif + } +}; + +class X { +public: + X() { + g(); +#if !PUREONLY + // expected-warning-re@-2 {{{{^}}Call to virtual function during construction}} + // expected-note-re@-3 {{{{^}}This constructor of an object of type 'X' has not returned when the virtual method was called}} + // expected-note-re@-4 {{{{^}}Call to virtual function during construction}} +#endif + } + X(int i) { + if (i > 0) { +#if !PUREONLY + // expected-note-re@-2 {{{{^}}Taking true branch}} + // expected-note-re@-3 {{{{^}}Taking false branch}} +#endif + X x(i - 1); +#if !PUREONLY + // expected-note-re@-2 {{{{^}}Calling constructor for 'X'}} +#endif + x.g(); // no warning + } + g(); +#if !PUREONLY + // expected-warning-re@-2 {{{{^}}Call to virtual function during construction}} + // expected-note-re@-3 {{{{^}}This constructor of an object of type 'X' has not returned when the virtual method was called}} + // expected-note-re@-4 {{{{^}}Call to virtual function during construction}} +#endif + } + virtual void g(); +}; + +class M; +class N { +public: + virtual void virtualMethod(); + void callFooOfM(M *); +}; +class M { +public: + M() { + N n; + n.virtualMethod(); // no warning + n.callFooOfM(this); +#if !PUREONLY + // expected-note-re@-2 {{{{^}}This constructor of an object of type 'M' has not returned when the virtual method was called}} + // expected-note-re@-3 {{{{^}}Calling 'N::callFooOfM'}} +#endif + } + virtual void foo(); +}; +void N::callFooOfM(M *m) { + m->foo(); +#if !PUREONLY + // expected-warning-re@-2 {{{{^}}Call to virtual function during construction}} + // expected-note-re@-3 {{{{^}}Call to virtual function during construction}} +#endif +} + +class Y { +public: + virtual void foobar(); + void fooY() { + F f1; + foobar(); +#if !PUREONLY + // expected-warning-re@-2 {{{{^}}Call to virtual function during construction}} + // expected-note-re@-3 {{{{^}}Call to virtual function during construction}} +#endif + } + Y() { fooY(); } +#if !PUREONLY + // expected-note-re@-2 {{{{^}}This constructor of an object of type 'Y' has not returned when the virtual method was called}} + // expected-note-re@-3 {{{{^}}Calling 'Y::fooY'}} +#endif +}; + int main() { - A *a; - B *b; - C *c; - D *d; - E *e; - F *f; + B b; +#if PUREONLY + //expected-note-re@-2 {{{{^}}Calling default constructor for 'B'}} +#else + //expected-note-re@-4 2{{{{^}}Calling default constructor for 'B'}} +#endif + C c; +#if !PUREONLY + //expected-note-re@-2 {{{{^}}Calling default constructor for 'C'}} +#endif + D d; + E e; + F f; + G g; + H h; + H h1(1); +#if !PUREONLY + //expected-note-re@-2 {{{{^}}Calling constructor for 'H'}} + //expected-note-re@-3 {{{{^}}Calling constructor for 'H'}} +#endif + X x; +#if !PUREONLY + //expected-note-re@-2 {{{{^}}Calling default constructor for 'X'}} +#endif + X x1(1); +#if !PUREONLY + //expected-note-re@-2 {{{{^}}Calling constructor for 'X'}} +#endif + M m; +#if !PUREONLY + //expected-note-re@-2 {{{{^}}Calling default constructor for 'M'}} +#endif + Y *y = new Y; + delete y; + header::Z z; +#if !PUREONLY + // expected-note-re@-2 {{{{^}}Calling default constructor for 'Z'}} +#endif } +#if !PUREONLY + //expected-note-re@-2 2{{{{^}}Calling '~E'}} +#endif -#include "virtualcall.h" +namespace PR34451 { +struct a { + void b() { + a c[1]; + c->b(); + } +}; -#define AS_SYSTEM -#include "virtualcall.h" -#undef AS_SYSTEM +class e { + public: + void b() const; +}; + +class c { + void m_fn2() const; + e d[]; +}; + +void c::m_fn2() const { d->b(); } +} diff --git a/test/Analysis/virtualcall.h b/test/Analysis/virtualcall.h index c2ad8a6444..e2fde2415e 100644 --- a/test/Analysis/virtualcall.h +++ b/test/Analysis/virtualcall.h @@ -1,36 +1,14 @@ -#ifdef AS_SYSTEM -#pragma clang system_header - -namespace system { - class A { - public: - A() { - foo(); // no-warning - } - - virtual int foo(); - }; -} - -#else - namespace header { - class A { + class Z { public: - A() { + Z() { foo(); #if !PUREONLY -#if INTERPROCEDURAL - // expected-warning-re@-3 {{{{^}}Call Path : fooCall to virtual function during construction will not dispatch to derived class}} -#else - // expected-warning-re@-5 {{{{^}}Call to virtual function during construction will not dispatch to derived class}} -#endif + // expected-warning-re@-2 {{{{^}}Call to virtual function during construction}} + // expected-note-re@-3 {{{{^}}This constructor of an object of type 'Z' has not returned when the virtual method was called}} + // expected-note-re@-4 {{{{^}}Call to virtual function during construction}} #endif - } - virtual int foo(); }; } - -#endif |