blob: 1d46f2a5d8150d839a8a1b69fcf209f2f857c74a [file] [log] [blame]
Manuel Klimekd00d6f12015-08-11 11:37:48 +00001//===---- IncludeInserterTest.cpp - clang-tidy ----------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Alexander Kornienko5f252ca2015-08-14 14:31:31 +000010#include "../clang-tidy/utils/IncludeInserter.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +000011#include "clang/Lex/Preprocessor.h"
12#include "clang/Frontend/CompilerInstance.h"
13#include "ClangTidyTest.h"
14#include "gtest/gtest.h"
15
Manuel Klimeke8c8d882015-08-11 14:21:26 +000016// FIXME: Canonicalize paths correctly on windows.
17// Currently, adding virtual files will canonicalize the paths before
18// storing the virtual entries.
19// When resolving virtual entries in the FileManager, the paths (for
20// example coming from a #include directive) are not canonicalized
21// to native paths; thus, the virtual file is not found.
22// This needs to be fixed in the FileManager before we can make
23// clang-tidy tests work.
24#if !defined(_WIN32)
25
Manuel Klimekd00d6f12015-08-11 11:37:48 +000026namespace clang {
27namespace tidy {
28namespace {
29
30class IncludeInserterCheckBase : public ClangTidyCheck {
31public:
Manuel Klimek46e82c32015-08-11 12:59:22 +000032 IncludeInserterCheckBase(StringRef CheckName, ClangTidyContext *Context)
33 : ClangTidyCheck(CheckName, Context) {}
Daniel Jasperd30dc3f2015-08-19 21:02:27 +000034
Manuel Klimekd00d6f12015-08-11 11:37:48 +000035 void registerPPCallbacks(CompilerInstance &Compiler) override {
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000036 Inserter.reset(new utils::IncludeInserter(
37 Compiler.getSourceManager(),
38 Compiler.getLangOpts(),
39 utils::IncludeSorter::IS_Google));
Manuel Klimekd00d6f12015-08-11 11:37:48 +000040 Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
41 }
42
43 void registerMatchers(ast_matchers::MatchFinder *Finder) override {
44 Finder->addMatcher(ast_matchers::declStmt().bind("stmt"), this);
45 }
46
47 void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
Alexander Kornienko9f58fe02016-12-13 16:19:19 +000048 auto Diag = diag(Result.Nodes.getNodeAs<DeclStmt>("stmt")->getLocStart(),
Daniel Jasperd30dc3f2015-08-19 21:02:27 +000049 "foo, bar");
50 for (StringRef header : HeadersToInclude()) {
51 auto Fixit = Inserter->CreateIncludeInsertion(
52 Result.SourceManager->getMainFileID(), header, IsAngledInclude());
53 if (Fixit) {
54 Diag << *Fixit;
55 }
Manuel Klimekd00d6f12015-08-11 11:37:48 +000056 }
Manuel Klimekd00d6f12015-08-11 11:37:48 +000057 }
58
Daniel Jasperd30dc3f2015-08-19 21:02:27 +000059 virtual std::vector<StringRef> HeadersToInclude() const = 0;
Manuel Klimekd00d6f12015-08-11 11:37:48 +000060 virtual bool IsAngledInclude() const = 0;
61
Etienne Bergeron2a4c00f2016-05-03 02:54:05 +000062 std::unique_ptr<utils::IncludeInserter> Inserter;
Manuel Klimekd00d6f12015-08-11 11:37:48 +000063};
64
65class NonSystemHeaderInserterCheck : public IncludeInserterCheckBase {
66public:
Manuel Klimek46e82c32015-08-11 12:59:22 +000067 NonSystemHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
68 : IncludeInserterCheckBase(CheckName, Context) {}
Daniel Jasperd30dc3f2015-08-19 21:02:27 +000069
70 std::vector<StringRef> HeadersToInclude() const override {
71 return {"path/to/header.h"};
72 }
73 bool IsAngledInclude() const override { return false; }
74};
75
76class MultipleHeaderInserterCheck : public IncludeInserterCheckBase {
77public:
78 MultipleHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
79 : IncludeInserterCheckBase(CheckName, Context) {}
Daniel Jasperd30dc3f2015-08-19 21:02:27 +000080
81 std::vector<StringRef> HeadersToInclude() const override {
82 return {"path/to/header.h", "path/to/header2.h", "path/to/header.h"};
83 }
Manuel Klimekd00d6f12015-08-11 11:37:48 +000084 bool IsAngledInclude() const override { return false; }
85};
86
Alexander Kornienko8cc024e2015-08-14 12:33:25 +000087class CSystemIncludeInserterCheck : public IncludeInserterCheckBase {
88public:
Alexander Kornienko078ab132015-08-14 13:23:55 +000089 CSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context)
90 : IncludeInserterCheckBase(CheckName, Context) {}
Daniel Jasperd30dc3f2015-08-19 21:02:27 +000091
92 std::vector<StringRef> HeadersToInclude() const override {
93 return {"stdlib.h"};
94 }
Alexander Kornienko8cc024e2015-08-14 12:33:25 +000095 bool IsAngledInclude() const override { return true; }
96};
97
Manuel Klimekd00d6f12015-08-11 11:37:48 +000098class CXXSystemIncludeInserterCheck : public IncludeInserterCheckBase {
99public:
Manuel Klimek46e82c32015-08-11 12:59:22 +0000100 CXXSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context)
101 : IncludeInserterCheckBase(CheckName, Context) {}
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000102
103 std::vector<StringRef> HeadersToInclude() const override { return {"set"}; }
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000104 bool IsAngledInclude() const override { return true; }
105};
106
107template <typename Check>
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000108std::string runCheckOnCode(StringRef Code, StringRef Filename) {
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000109 std::vector<ClangTidyError> Errors;
110 return test::runCheckOnCode<Check>(Code, &Errors, Filename, None,
111 ClangTidyOptions(),
112 {// Main file include
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000113 {"clang_tidy/tests/"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000114 "insert_includes_test_header.h",
115 "\n"},
116 // Non system headers
117 {"path/to/a/header.h", "\n"},
118 {"path/to/z/header.h", "\n"},
119 {"path/to/header.h", "\n"},
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000120 {"path/to/header2.h", "\n"},
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000121 // Fake system headers.
122 {"stdlib.h", "\n"},
123 {"unistd.h", "\n"},
124 {"list", "\n"},
125 {"map", "\n"},
126 {"set", "\n"},
127 {"vector", "\n"}});
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000128}
129
130TEST(IncludeInserterTest, InsertAfterLastNonSystemInclude) {
131 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000132#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000133
134#include <list>
135#include <map>
136
137#include "path/to/a/header.h"
138
139void foo() {
140 int a = 0;
141})";
142 const char *PostCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000143#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000144
145#include <list>
146#include <map>
147
148#include "path/to/a/header.h"
149#include "path/to/header.h"
150
151void foo() {
152 int a = 0;
153})";
154
155 EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000156 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000157 "insert_includes_test_input2.cc"));
158}
159
160TEST(IncludeInserterTest, InsertMultipleIncludesAndDeduplicate) {
161 const char *PreCode = R"(
162#include "clang_tidy/tests/insert_includes_test_header.h"
163
164#include <list>
165#include <map>
166
167#include "path/to/a/header.h"
168
169void foo() {
170 int a = 0;
171})";
172 const char *PostCode = R"(
173#include "clang_tidy/tests/insert_includes_test_header.h"
174
175#include <list>
176#include <map>
177
178#include "path/to/a/header.h"
179#include "path/to/header.h"
180#include "path/to/header2.h"
181
182void foo() {
183 int a = 0;
184})";
185
186 EXPECT_EQ(PostCode, runCheckOnCode<MultipleHeaderInserterCheck>(
187 PreCode, "clang_tidy/tests/"
188 "insert_includes_test_input2.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000189}
190
191TEST(IncludeInserterTest, InsertBeforeFirstNonSystemInclude) {
192 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000193#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000194
195#include <list>
196#include <map>
197
198#include "path/to/z/header.h"
199
200void foo() {
201 int a = 0;
202})";
203 const char *PostCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000204#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000205
206#include <list>
207#include <map>
208
209#include "path/to/header.h"
210#include "path/to/z/header.h"
211
212void foo() {
213 int a = 0;
214})";
215
216 EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000217 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000218 "insert_includes_test_input2.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000219}
220
221TEST(IncludeInserterTest, InsertBetweenNonSystemIncludes) {
222 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000223#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000224
225#include <list>
226#include <map>
227
228#include "path/to/a/header.h"
229#include "path/to/z/header.h"
230
231void foo() {
232 int a = 0;
233})";
234 const char *PostCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000235#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000236
237#include <list>
238#include <map>
239
240#include "path/to/a/header.h"
241#include "path/to/header.h"
242#include "path/to/z/header.h"
243
244void foo() {
245 int a = 0;
246})";
247
248 EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000249 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000250 "insert_includes_test_input2.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000251}
252
253TEST(IncludeInserterTest, NonSystemIncludeAlreadyIncluded) {
254 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000255#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000256
257#include <list>
258#include <map>
259
260#include "path/to/a/header.h"
261#include "path/to/header.h"
262#include "path/to/z/header.h"
263
264void foo() {
265 int a = 0;
266})";
267 EXPECT_EQ(PreCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000268 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000269 "insert_includes_test_input2.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000270}
271
272TEST(IncludeInserterTest, InsertNonSystemIncludeAfterLastCXXSystemInclude) {
273 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000274#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000275
276#include <list>
277#include <map>
278
279void foo() {
280 int a = 0;
281})";
282 const char *PostCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000283#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000284
285#include <list>
286#include <map>
287
288#include "path/to/header.h"
289
290void foo() {
291 int a = 0;
292})";
293
294 EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000295 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000296 "insert_includes_test_header.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000297}
298
299TEST(IncludeInserterTest, InsertNonSystemIncludeAfterMainFileInclude) {
300 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000301#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000302
303void foo() {
304 int a = 0;
305})";
306 const char *PostCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000307#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000308
309#include "path/to/header.h"
310
311void foo() {
312 int a = 0;
313})";
314
315 EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000316 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000317 "insert_includes_test_header.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000318}
319
320TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterLastCXXSystemInclude) {
321 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000322#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000323
324#include <list>
325#include <map>
326
327#include "path/to/a/header.h"
328
329void foo() {
330 int a = 0;
331})";
332 const char *PostCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000333#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000334
335#include <list>
336#include <map>
337#include <set>
338
339#include "path/to/a/header.h"
340
341void foo() {
342 int a = 0;
343})";
344
345 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000346 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000347 "insert_includes_test_header.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000348}
349
350TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeFirstCXXSystemInclude) {
351 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000352#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000353
354#include <vector>
355
356#include "path/to/a/header.h"
357
358void foo() {
359 int a = 0;
360})";
361 const char *PostCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000362#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000363
364#include <set>
365#include <vector>
366
367#include "path/to/a/header.h"
368
369void foo() {
370 int a = 0;
371})";
372
373 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000374 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000375 "insert_includes_test_header.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000376}
377
378TEST(IncludeInserterTest, InsertCXXSystemIncludeBetweenCXXSystemIncludes) {
379 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000380#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000381
382#include <map>
383#include <vector>
384
385#include "path/to/a/header.h"
386
387void foo() {
388 int a = 0;
389})";
390 const char *PostCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000391#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000392
393#include <map>
394#include <set>
395#include <vector>
396
397#include "path/to/a/header.h"
398
399void foo() {
400 int a = 0;
401})";
402
403 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000404 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000405 "insert_includes_test_header.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000406}
407
408TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterMainFileInclude) {
409 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000410#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000411
412#include "path/to/a/header.h"
413
414void foo() {
415 int a = 0;
416})";
417 const char *PostCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000418#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000419
420#include <set>
421
422#include "path/to/a/header.h"
423
424void foo() {
425 int a = 0;
426})";
427
428 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000429 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000430 "insert_includes_test_header.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000431}
432
433TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterCSystemInclude) {
434 const char *PreCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000435#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000436
437#include <stdlib.h>
438
439#include "path/to/a/header.h"
440
441void foo() {
442 int a = 0;
443})";
444 const char *PostCode = R"(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000445#include "clang_tidy/tests/insert_includes_test_header.h"
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000446
447#include <stdlib.h>
448
449#include <set>
450
451#include "path/to/a/header.h"
452
453void foo() {
454 int a = 0;
455})";
456
457 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000458 PreCode, "clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000459 "insert_includes_test_header.cc"));
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000460}
461
Alexander Kornienko8cc024e2015-08-14 12:33:25 +0000462TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeNonSystemInclude) {
463 const char *PreCode = R"(
464#include "path/to/a/header.h"
465
466void foo() {
467 int a = 0;
468})";
469 const char *PostCode = R"(
470#include <set>
471
472#include "path/to/a/header.h"
473
474void foo() {
475 int a = 0;
476})";
477
478 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
479 PreCode, "devtools/cymbal/clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000480 "insert_includes_test_header.cc"));
Alexander Kornienko8cc024e2015-08-14 12:33:25 +0000481}
482
483TEST(IncludeInserterTest, InsertCSystemIncludeBeforeCXXSystemInclude) {
484 const char *PreCode = R"(
485#include <set>
486
487#include "path/to/a/header.h"
488
489void foo() {
490 int a = 0;
491})";
492 const char *PostCode = R"(
493#include <stdlib.h>
494
495#include <set>
496
497#include "path/to/a/header.h"
498
499void foo() {
500 int a = 0;
501})";
502
503 EXPECT_EQ(PostCode, runCheckOnCode<CSystemIncludeInserterCheck>(
504 PreCode, "devtools/cymbal/clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000505 "insert_includes_test_header.cc"));
Alexander Kornienko8cc024e2015-08-14 12:33:25 +0000506}
507
508TEST(IncludeInserterTest, InsertIncludeIfThereWasNoneBefore) {
509 const char *PreCode = R"(
510void foo() {
511 int a = 0;
512})";
513 const char *PostCode = R"(#include <set>
514
515
516void foo() {
517 int a = 0;
518})";
519
520 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
521 PreCode, "devtools/cymbal/clang_tidy/tests/"
Daniel Jasperd30dc3f2015-08-19 21:02:27 +0000522 "insert_includes_test_header.cc"));
Alexander Kornienko8cc024e2015-08-14 12:33:25 +0000523}
524
Eugene Zelenko7da47b82016-01-26 22:32:24 +0000525} // anonymous namespace
Manuel Klimekd00d6f12015-08-11 11:37:48 +0000526} // namespace tidy
527} // namespace clang
Manuel Klimeke8c8d882015-08-11 14:21:26 +0000528
Alexander Kornienko078ab132015-08-14 13:23:55 +0000529#endif