xref: /llvm-project/llvm/unittests/ADT/FallibleIteratorTest.cpp (revision 3e040e05f89c058bb6e6a88c4e9ffccf1185b084)
1*3e040e05SLang Hames //===- unittests/ADT/FallibleIteratorTest.cpp - fallible_iterator.h tests -===//
2*3e040e05SLang Hames //
3*3e040e05SLang Hames // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*3e040e05SLang Hames // See https://llvm.org/LICENSE.txt for license information.
5*3e040e05SLang Hames // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*3e040e05SLang Hames //
7*3e040e05SLang Hames //===----------------------------------------------------------------------===//
8*3e040e05SLang Hames 
9*3e040e05SLang Hames #include "llvm/ADT/fallible_iterator.h"
10*3e040e05SLang Hames #include "llvm/Testing/Support/Error.h"
11*3e040e05SLang Hames 
12*3e040e05SLang Hames #include "gtest/gtest-spi.h"
13*3e040e05SLang Hames #include "gtest/gtest.h"
14*3e040e05SLang Hames 
15*3e040e05SLang Hames #include <utility>
16*3e040e05SLang Hames #include <vector>
17*3e040e05SLang Hames 
18*3e040e05SLang Hames using namespace llvm;
19*3e040e05SLang Hames 
20*3e040e05SLang Hames namespace {
21*3e040e05SLang Hames 
22*3e040e05SLang Hames using ItemValid = enum { ValidItem, InvalidItem };
23*3e040e05SLang Hames using LinkValid = enum { ValidLink, InvalidLink };
24*3e040e05SLang Hames 
25*3e040e05SLang Hames class Item {
26*3e040e05SLang Hames public:
Item(ItemValid V)27*3e040e05SLang Hames   Item(ItemValid V) : V(V) {}
isValid() const28*3e040e05SLang Hames   bool isValid() const { return V == ValidItem; }
29*3e040e05SLang Hames 
30*3e040e05SLang Hames private:
31*3e040e05SLang Hames   ItemValid V;
32*3e040e05SLang Hames };
33*3e040e05SLang Hames 
34*3e040e05SLang Hames // A utility to mock "bad collections". It supports both invalid items,
35*3e040e05SLang Hames // where the dereference operator may return an Error, and bad links
36*3e040e05SLang Hames // where the inc/dec operations may return an Error.
37*3e040e05SLang Hames // Each element of the mock collection contains a pair of a (possibly broken)
38*3e040e05SLang Hames // item and link.
39*3e040e05SLang Hames using FallibleCollection = std::vector<std::pair<Item, LinkValid>>;
40*3e040e05SLang Hames 
41*3e040e05SLang Hames class FallibleCollectionWalker {
42*3e040e05SLang Hames public:
FallibleCollectionWalker(FallibleCollection & C,unsigned Idx)43*3e040e05SLang Hames   FallibleCollectionWalker(FallibleCollection &C, unsigned Idx)
44*3e040e05SLang Hames       : C(C), Idx(Idx) {}
45*3e040e05SLang Hames 
operator *()46*3e040e05SLang Hames   Item &operator*() { return C[Idx].first; }
47*3e040e05SLang Hames 
operator *() const48*3e040e05SLang Hames   const Item &operator*() const { return C[Idx].first; }
49*3e040e05SLang Hames 
inc()50*3e040e05SLang Hames   Error inc() {
51*3e040e05SLang Hames     assert(Idx != C.size() && "Walking off end of (mock) collection");
52*3e040e05SLang Hames     if (C[Idx].second == ValidLink) {
53*3e040e05SLang Hames       ++Idx;
54*3e040e05SLang Hames       return Error::success();
55*3e040e05SLang Hames     }
56*3e040e05SLang Hames     return make_error<StringError>("cant get next object in (mock) collection",
57*3e040e05SLang Hames                                    inconvertibleErrorCode());
58*3e040e05SLang Hames   }
59*3e040e05SLang Hames 
dec()60*3e040e05SLang Hames   Error dec() {
61*3e040e05SLang Hames     assert(Idx != 0 && "Walking off start of (mock) collection");
62*3e040e05SLang Hames     --Idx;
63*3e040e05SLang Hames     if (C[Idx].second == ValidLink)
64*3e040e05SLang Hames       return Error::success();
65*3e040e05SLang Hames     return make_error<StringError>("cant get prev object in (mock) collection",
66*3e040e05SLang Hames                                    inconvertibleErrorCode());
67*3e040e05SLang Hames   }
68*3e040e05SLang Hames 
operator ==(const FallibleCollectionWalker & LHS,const FallibleCollectionWalker & RHS)69*3e040e05SLang Hames   friend bool operator==(const FallibleCollectionWalker &LHS,
70*3e040e05SLang Hames                          const FallibleCollectionWalker &RHS) {
71*3e040e05SLang Hames     assert(&LHS.C == &RHS.C && "Comparing iterators across collectionss.");
72*3e040e05SLang Hames     return LHS.Idx == RHS.Idx;
73*3e040e05SLang Hames   }
74*3e040e05SLang Hames 
75*3e040e05SLang Hames private:
76*3e040e05SLang Hames   FallibleCollection &C;
77*3e040e05SLang Hames   unsigned Idx;
78*3e040e05SLang Hames };
79*3e040e05SLang Hames 
80*3e040e05SLang Hames class FallibleCollectionWalkerWithStructDeref
81*3e040e05SLang Hames     : public FallibleCollectionWalker {
82*3e040e05SLang Hames public:
83*3e040e05SLang Hames   using FallibleCollectionWalker::FallibleCollectionWalker;
84*3e040e05SLang Hames 
operator ->()85*3e040e05SLang Hames   Item *operator->() { return &this->operator*(); }
86*3e040e05SLang Hames 
operator ->() const87*3e040e05SLang Hames   const Item *operator->() const { return &this->operator*(); }
88*3e040e05SLang Hames };
89*3e040e05SLang Hames 
90*3e040e05SLang Hames class FallibleCollectionWalkerWithFallibleDeref
91*3e040e05SLang Hames     : public FallibleCollectionWalker {
92*3e040e05SLang Hames public:
93*3e040e05SLang Hames   using FallibleCollectionWalker::FallibleCollectionWalker;
94*3e040e05SLang Hames 
operator *()95*3e040e05SLang Hames   Expected<Item &> operator*() {
96*3e040e05SLang Hames     auto &I = FallibleCollectionWalker::operator*();
97*3e040e05SLang Hames     if (!I.isValid())
98*3e040e05SLang Hames       return make_error<StringError>("bad item", inconvertibleErrorCode());
99*3e040e05SLang Hames     return I;
100*3e040e05SLang Hames   }
101*3e040e05SLang Hames 
operator *() const102*3e040e05SLang Hames   Expected<const Item &> operator*() const {
103*3e040e05SLang Hames     const auto &I = FallibleCollectionWalker::operator*();
104*3e040e05SLang Hames     if (!I.isValid())
105*3e040e05SLang Hames       return make_error<StringError>("bad item", inconvertibleErrorCode());
106*3e040e05SLang Hames     return I;
107*3e040e05SLang Hames   }
108*3e040e05SLang Hames };
109*3e040e05SLang Hames 
TEST(FallibleIteratorTest,BasicSuccess)110*3e040e05SLang Hames TEST(FallibleIteratorTest, BasicSuccess) {
111*3e040e05SLang Hames 
112*3e040e05SLang Hames   // Check that a basic use-case involing successful iteration over a
113*3e040e05SLang Hames   // "FallibleCollection" works.
114*3e040e05SLang Hames 
115*3e040e05SLang Hames   FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, ValidLink}});
116*3e040e05SLang Hames 
117*3e040e05SLang Hames   FallibleCollectionWalker begin(C, 0);
118*3e040e05SLang Hames   FallibleCollectionWalker end(C, 2);
119*3e040e05SLang Hames 
120*3e040e05SLang Hames   Error Err = Error::success();
121*3e040e05SLang Hames   for (auto &Elem :
122*3e040e05SLang Hames        make_fallible_range<FallibleCollectionWalker>(begin, end, Err))
123*3e040e05SLang Hames     EXPECT_TRUE(Elem.isValid());
124*3e040e05SLang Hames   cantFail(std::move(Err));
125*3e040e05SLang Hames }
126*3e040e05SLang Hames 
TEST(FallibleIteratorTest,BasicFailure)127*3e040e05SLang Hames TEST(FallibleIteratorTest, BasicFailure) {
128*3e040e05SLang Hames 
129*3e040e05SLang Hames   // Check that a iteration failure (due to the InvalidLink state on element one
130*3e040e05SLang Hames   // of the fallible collection) breaks out of the loop and raises an Error.
131*3e040e05SLang Hames 
132*3e040e05SLang Hames   FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, InvalidLink}});
133*3e040e05SLang Hames 
134*3e040e05SLang Hames   FallibleCollectionWalker begin(C, 0);
135*3e040e05SLang Hames   FallibleCollectionWalker end(C, 2);
136*3e040e05SLang Hames 
137*3e040e05SLang Hames   Error Err = Error::success();
138*3e040e05SLang Hames   for (auto &Elem :
139*3e040e05SLang Hames        make_fallible_range<FallibleCollectionWalker>(begin, end, Err))
140*3e040e05SLang Hames     EXPECT_TRUE(Elem.isValid());
141*3e040e05SLang Hames 
142*3e040e05SLang Hames   EXPECT_THAT_ERROR(std::move(Err), Failed()) << "Expected failure value";
143*3e040e05SLang Hames }
144*3e040e05SLang Hames 
TEST(FallibleIteratorTest,NoRedundantErrorCheckOnEarlyExit)145*3e040e05SLang Hames TEST(FallibleIteratorTest, NoRedundantErrorCheckOnEarlyExit) {
146*3e040e05SLang Hames 
147*3e040e05SLang Hames   // Check that an early return from the loop body does not require a redundant
148*3e040e05SLang Hames   // check of Err.
149*3e040e05SLang Hames 
150*3e040e05SLang Hames   FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, ValidLink}});
151*3e040e05SLang Hames 
152*3e040e05SLang Hames   FallibleCollectionWalker begin(C, 0);
153*3e040e05SLang Hames   FallibleCollectionWalker end(C, 2);
154*3e040e05SLang Hames 
155*3e040e05SLang Hames   Error Err = Error::success();
156*3e040e05SLang Hames   for (auto &Elem :
157*3e040e05SLang Hames        make_fallible_range<FallibleCollectionWalker>(begin, end, Err)) {
158*3e040e05SLang Hames     (void)Elem;
159*3e040e05SLang Hames     return;
160*3e040e05SLang Hames   }
161*3e040e05SLang Hames   // Err not checked, but should be ok because we exit from the loop
162*3e040e05SLang Hames   // body.
163*3e040e05SLang Hames }
164*3e040e05SLang Hames 
165*3e040e05SLang Hames #if LLVM_ENABLE_ABI_BREAKING_CHECKS
TEST(FallibleIteratorTest,RegularLoopExitRequiresErrorCheck)166*3e040e05SLang Hames TEST(FallibleIteratorTest, RegularLoopExitRequiresErrorCheck) {
167*3e040e05SLang Hames 
168*3e040e05SLang Hames   // Check that Err must be checked after a normal (i.e. not early) loop exit
169*3e040e05SLang Hames   // by failing to check and expecting program death (due to the unchecked
170*3e040e05SLang Hames   // error).
171*3e040e05SLang Hames 
172*3e040e05SLang Hames   EXPECT_DEATH(
173*3e040e05SLang Hames       {
174*3e040e05SLang Hames         FallibleCollection C({{ValidItem, ValidLink}, {ValidItem, ValidLink}});
175*3e040e05SLang Hames 
176*3e040e05SLang Hames         FallibleCollectionWalker begin(C, 0);
177*3e040e05SLang Hames         FallibleCollectionWalker end(C, 2);
178*3e040e05SLang Hames 
179*3e040e05SLang Hames         Error Err = Error::success();
180*3e040e05SLang Hames         for (auto &Elem :
181*3e040e05SLang Hames              make_fallible_range<FallibleCollectionWalker>(begin, end, Err))
182*3e040e05SLang Hames           (void)Elem;
183*3e040e05SLang Hames       },
184*3e040e05SLang Hames       "Program aborted due to an unhandled Error:")
185*3e040e05SLang Hames       << "Normal (i.e. not early) loop exit should require an error check";
186*3e040e05SLang Hames }
187*3e040e05SLang Hames #endif
188*3e040e05SLang Hames 
TEST(FallibleIteratorTest,RawIncrementAndDecrementBehavior)189*3e040e05SLang Hames TEST(FallibleIteratorTest, RawIncrementAndDecrementBehavior) {
190*3e040e05SLang Hames 
191*3e040e05SLang Hames   // Check the exact behavior of increment / decrement.
192*3e040e05SLang Hames 
193*3e040e05SLang Hames   FallibleCollection C({{ValidItem, ValidLink},
194*3e040e05SLang Hames                         {ValidItem, InvalidLink},
195*3e040e05SLang Hames                         {ValidItem, ValidLink},
196*3e040e05SLang Hames                         {ValidItem, InvalidLink}});
197*3e040e05SLang Hames 
198*3e040e05SLang Hames   {
199*3e040e05SLang Hames     // One increment from begin succeeds.
200*3e040e05SLang Hames     Error Err = Error::success();
201*3e040e05SLang Hames     auto I = make_fallible_itr(FallibleCollectionWalker(C, 0), Err);
202*3e040e05SLang Hames     ++I;
203*3e040e05SLang Hames     EXPECT_THAT_ERROR(std::move(Err), Succeeded());
204*3e040e05SLang Hames   }
205*3e040e05SLang Hames 
206*3e040e05SLang Hames   {
207*3e040e05SLang Hames     // Two increments from begin fail.
208*3e040e05SLang Hames     Error Err = Error::success();
209*3e040e05SLang Hames     auto I = make_fallible_itr(FallibleCollectionWalker(C, 0), Err);
210*3e040e05SLang Hames     ++I;
211*3e040e05SLang Hames     EXPECT_THAT_ERROR(std::move(Err), Succeeded());
212*3e040e05SLang Hames     ++I;
213*3e040e05SLang Hames     EXPECT_THAT_ERROR(std::move(Err), Failed()) << "Expected failure value";
214*3e040e05SLang Hames   }
215*3e040e05SLang Hames 
216*3e040e05SLang Hames   {
217*3e040e05SLang Hames     // One decement from element three succeeds.
218*3e040e05SLang Hames     Error Err = Error::success();
219*3e040e05SLang Hames     auto I = make_fallible_itr(FallibleCollectionWalker(C, 3), Err);
220*3e040e05SLang Hames     --I;
221*3e040e05SLang Hames     EXPECT_THAT_ERROR(std::move(Err), Succeeded());
222*3e040e05SLang Hames   }
223*3e040e05SLang Hames 
224*3e040e05SLang Hames   {
225*3e040e05SLang Hames     // One decement from element three succeeds.
226*3e040e05SLang Hames     Error Err = Error::success();
227*3e040e05SLang Hames     auto I = make_fallible_itr(FallibleCollectionWalker(C, 3), Err);
228*3e040e05SLang Hames     --I;
229*3e040e05SLang Hames     EXPECT_THAT_ERROR(std::move(Err), Succeeded());
230*3e040e05SLang Hames     --I;
231*3e040e05SLang Hames     EXPECT_THAT_ERROR(std::move(Err), Failed());
232*3e040e05SLang Hames   }
233*3e040e05SLang Hames }
234*3e040e05SLang Hames 
TEST(FallibleIteratorTest,CheckStructDerefOperatorSupport)235*3e040e05SLang Hames TEST(FallibleIteratorTest, CheckStructDerefOperatorSupport) {
236*3e040e05SLang Hames   // Check that the fallible_iterator wrapper forwards through to the
237*3e040e05SLang Hames   // underlying iterator's structure dereference operator if present.
238*3e040e05SLang Hames 
239*3e040e05SLang Hames   FallibleCollection C({{ValidItem, ValidLink},
240*3e040e05SLang Hames                         {ValidItem, ValidLink},
241*3e040e05SLang Hames                         {InvalidItem, InvalidLink}});
242*3e040e05SLang Hames 
243*3e040e05SLang Hames   FallibleCollectionWalkerWithStructDeref begin(C, 0);
244*3e040e05SLang Hames 
245*3e040e05SLang Hames   {
246*3e040e05SLang Hames     Error Err = Error::success();
247*3e040e05SLang Hames     auto I = make_fallible_itr(begin, Err);
248*3e040e05SLang Hames     EXPECT_TRUE(I->isValid());
249*3e040e05SLang Hames     cantFail(std::move(Err));
250*3e040e05SLang Hames   }
251*3e040e05SLang Hames 
252*3e040e05SLang Hames   {
253*3e040e05SLang Hames     Error Err = Error::success();
254*3e040e05SLang Hames     const auto I = make_fallible_itr(begin, Err);
255*3e040e05SLang Hames     EXPECT_TRUE(I->isValid());
256*3e040e05SLang Hames     cantFail(std::move(Err));
257*3e040e05SLang Hames   }
258*3e040e05SLang Hames }
259*3e040e05SLang Hames 
TEST(FallibleIteratorTest,CheckDerefToExpectedSupport)260*3e040e05SLang Hames TEST(FallibleIteratorTest, CheckDerefToExpectedSupport) {
261*3e040e05SLang Hames 
262*3e040e05SLang Hames   // Check that the fallible_iterator wrapper forwards value types, in
263*3e040e05SLang Hames   // particular llvm::Expected, correctly.
264*3e040e05SLang Hames 
265*3e040e05SLang Hames   FallibleCollection C({{ValidItem, ValidLink},
266*3e040e05SLang Hames                         {InvalidItem, ValidLink},
267*3e040e05SLang Hames                         {ValidItem, ValidLink}});
268*3e040e05SLang Hames 
269*3e040e05SLang Hames   FallibleCollectionWalkerWithFallibleDeref begin(C, 0);
270*3e040e05SLang Hames   FallibleCollectionWalkerWithFallibleDeref end(C, 3);
271*3e040e05SLang Hames 
272*3e040e05SLang Hames   Error Err = Error::success();
273*3e040e05SLang Hames   auto I = make_fallible_itr(begin, Err);
274*3e040e05SLang Hames   auto E = make_fallible_end(end);
275*3e040e05SLang Hames 
276*3e040e05SLang Hames   Expected<Item> V1 = *I;
277*3e040e05SLang Hames   EXPECT_THAT_ERROR(V1.takeError(), Succeeded());
278*3e040e05SLang Hames   ++I;
279*3e040e05SLang Hames   EXPECT_NE(I, E); // Implicitly check error.
280*3e040e05SLang Hames   Expected<Item> V2 = *I;
281*3e040e05SLang Hames   EXPECT_THAT_ERROR(V2.takeError(), Failed());
282*3e040e05SLang Hames   ++I;
283*3e040e05SLang Hames   EXPECT_NE(I, E); // Implicitly check error.
284*3e040e05SLang Hames   Expected<Item> V3 = *I;
285*3e040e05SLang Hames   EXPECT_THAT_ERROR(V3.takeError(), Succeeded());
286*3e040e05SLang Hames   ++I;
287*3e040e05SLang Hames   EXPECT_EQ(I, E);
288*3e040e05SLang Hames   cantFail(std::move(Err));
289*3e040e05SLang Hames }
290*3e040e05SLang Hames 
291*3e040e05SLang Hames } // namespace
292