diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:44:14 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:44:14 +0000 |
commit | 2b6b257f4e5503a7a2675bdb8735693db769f75c (patch) | |
tree | e85e046ae7003fe3bcc8b5454cd0fa3f7407b470 /unittests/Lex | |
parent | b4348ed0b7e90c0831b925fbee00b5f179a99796 (diff) |
Notes
Diffstat (limited to 'unittests/Lex')
-rw-r--r-- | unittests/Lex/CMakeLists.txt | 1 | ||||
-rw-r--r-- | unittests/Lex/HeaderMapTest.cpp | 258 | ||||
-rw-r--r-- | unittests/Lex/LexerTest.cpp | 30 | ||||
-rw-r--r-- | unittests/Lex/Makefile | 16 | ||||
-rw-r--r-- | unittests/Lex/PPConditionalDirectiveRecordTest.cpp | 4 |
5 files changed, 287 insertions, 22 deletions
diff --git a/unittests/Lex/CMakeLists.txt b/unittests/Lex/CMakeLists.txt index 461e0d95fc87e..ef0f06c0b3c9d 100644 --- a/unittests/Lex/CMakeLists.txt +++ b/unittests/Lex/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS ) add_clang_unittest(LexTests + HeaderMapTest.cpp LexerTest.cpp PPCallbacksTest.cpp PPConditionalDirectiveRecordTest.cpp diff --git a/unittests/Lex/HeaderMapTest.cpp b/unittests/Lex/HeaderMapTest.cpp new file mode 100644 index 0000000000000..d16efe82c19da --- /dev/null +++ b/unittests/Lex/HeaderMapTest.cpp @@ -0,0 +1,258 @@ +//===- unittests/Lex/HeaderMapTest.cpp - HeaderMap tests ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------===// + +#include "clang/Basic/CharInfo.h" +#include "clang/Lex/HeaderMap.h" +#include "clang/Lex/HeaderMapTypes.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/SwapByteOrder.h" +#include "gtest/gtest.h" +#include <cassert> +#include <type_traits> + +using namespace clang; +using namespace llvm; + +namespace { + +// Lay out a header file for testing. +template <unsigned NumBuckets, unsigned NumBytes> struct MapFile { + HMapHeader Header; + HMapBucket Buckets[NumBuckets]; + unsigned char Bytes[NumBytes]; + + void init() { + memset(this, 0, sizeof(MapFile)); + Header.Magic = HMAP_HeaderMagicNumber; + Header.Version = HMAP_HeaderVersion; + Header.NumBuckets = NumBuckets; + Header.StringsOffset = sizeof(Header) + sizeof(Buckets); + } + + void swapBytes() { + using llvm::sys::getSwappedBytes; + Header.Magic = getSwappedBytes(Header.Magic); + Header.Version = getSwappedBytes(Header.Version); + Header.NumBuckets = getSwappedBytes(Header.NumBuckets); + Header.StringsOffset = getSwappedBytes(Header.StringsOffset); + } + + std::unique_ptr<const MemoryBuffer> getBuffer() const { + return MemoryBuffer::getMemBuffer( + StringRef(reinterpret_cast<const char *>(this), sizeof(MapFile)), + "header", + /* RequresNullTerminator */ false); + } +}; + +// The header map hash function. +static inline unsigned getHash(StringRef Str) { + unsigned Result = 0; + for (char C : Str) + Result += toLowercase(C) * 13; + return Result; +} + +template <class FileTy> struct FileMaker { + FileTy &File; + unsigned SI = 1; + unsigned BI = 0; + FileMaker(FileTy &File) : File(File) {} + + unsigned addString(StringRef S) { + assert(SI + S.size() + 1 <= sizeof(File.Bytes)); + std::copy(S.begin(), S.end(), File.Bytes + SI); + auto OldSI = SI; + SI += S.size() + 1; + return OldSI; + } + void addBucket(unsigned Hash, unsigned Key, unsigned Prefix, unsigned Suffix) { + assert(!(File.Header.NumBuckets & (File.Header.NumBuckets - 1))); + unsigned I = Hash & (File.Header.NumBuckets - 1); + do { + if (!File.Buckets[I].Key) { + File.Buckets[I].Key = Key; + File.Buckets[I].Prefix = Prefix; + File.Buckets[I].Suffix = Suffix; + ++File.Header.NumEntries; + return; + } + ++I; + I &= File.Header.NumBuckets - 1; + } while (I != (Hash & (File.Header.NumBuckets - 1))); + llvm_unreachable("no empty buckets"); + } +}; + +TEST(HeaderMapTest, checkHeaderEmpty) { + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader( + *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap)); + ASSERT_FALSE(HeaderMapImpl::checkHeader( + *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap)); +} + +TEST(HeaderMapTest, checkHeaderMagic) { + MapFile<1, 1> File; + File.init(); + File.Header.Magic = 0; + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + +TEST(HeaderMapTest, checkHeaderReserved) { + MapFile<1, 1> File; + File.init(); + File.Header.Reserved = 1; + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + +TEST(HeaderMapTest, checkHeaderVersion) { + MapFile<1, 1> File; + File.init(); + ++File.Header.Version; + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + +TEST(HeaderMapTest, checkHeaderValidButEmpty) { + MapFile<1, 1> File; + File.init(); + bool NeedsSwap; + ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); + ASSERT_FALSE(NeedsSwap); + + File.swapBytes(); + ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); + ASSERT_TRUE(NeedsSwap); +} + +TEST(HeaderMapTest, checkHeader3Buckets) { + MapFile<3, 1> File; + ASSERT_EQ(3 * sizeof(HMapBucket), sizeof(File.Buckets)); + + File.init(); + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + +TEST(HeaderMapTest, checkHeader0Buckets) { + // Create with 1 bucket to avoid 0-sized arrays. + MapFile<1, 1> File; + File.init(); + File.Header.NumBuckets = 0; + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + +TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) { + MapFile<1, 1> File; + File.init(); + File.Header.NumBuckets = 8; + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + +TEST(HeaderMapTest, lookupFilename) { + typedef MapFile<2, 7> FileTy; + FileTy File; + File.init(); + + FileMaker<FileTy> Maker(File); + auto a = Maker.addString("a"); + auto b = Maker.addString("b"); + auto c = Maker.addString("c"); + Maker.addBucket(getHash("a"), a, b, c); + + bool NeedsSwap; + ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); + ASSERT_FALSE(NeedsSwap); + HeaderMapImpl Map(File.getBuffer(), NeedsSwap); + + SmallString<8> DestPath; + ASSERT_EQ("bc", Map.lookupFilename("a", DestPath)); +} + +template <class FileTy, class PaddingTy> struct PaddedFile { + FileTy File; + PaddingTy Padding; +}; + +TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) { + typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy; + static_assert(std::is_standard_layout<FileTy>::value, + "Expected standard layout"); + static_assert(sizeof(FileTy) == 64, "check the math"); + PaddedFile<FileTy, uint64_t> P; + auto &File = P.File; + auto &Padding = P.Padding; + File.init(); + + FileMaker<FileTy> Maker(File); + auto a = Maker.addString("a"); + auto b = Maker.addString("b"); + auto c = Maker.addString("c"); + Maker.addBucket(getHash("a"), a, b, c); + + // Add 'x' characters to cause an overflow into Padding. + ASSERT_EQ('c', File.Bytes[5]); + for (unsigned I = 6; I < sizeof(File.Bytes); ++I) { + ASSERT_EQ(0, File.Bytes[I]); + File.Bytes[I] = 'x'; + } + Padding = 0xffffffff; // Padding won't stop it either. + + bool NeedsSwap; + ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); + ASSERT_FALSE(NeedsSwap); + HeaderMapImpl Map(File.getBuffer(), NeedsSwap); + + // The string for "c" runs to the end of File. Check that the suffix + // ("cxxxx...") is detected as truncated, and an empty string is returned. + SmallString<24> DestPath; + ASSERT_EQ("", Map.lookupFilename("a", DestPath)); +} + +TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) { + typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy; + static_assert(std::is_standard_layout<FileTy>::value, + "Expected standard layout"); + static_assert(sizeof(FileTy) == 64, "check the math"); + PaddedFile<FileTy, uint64_t> P; + auto &File = P.File; + auto &Padding = P.Padding; + File.init(); + + FileMaker<FileTy> Maker(File); + auto a = Maker.addString("a"); + auto c = Maker.addString("c"); + auto b = Maker.addString("b"); // Store the prefix last. + Maker.addBucket(getHash("a"), a, b, c); + + // Add 'x' characters to cause an overflow into Padding. + ASSERT_EQ('b', File.Bytes[5]); + for (unsigned I = 6; I < sizeof(File.Bytes); ++I) { + ASSERT_EQ(0, File.Bytes[I]); + File.Bytes[I] = 'x'; + } + Padding = 0xffffffff; // Padding won't stop it either. + + bool NeedsSwap; + ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); + ASSERT_FALSE(NeedsSwap); + HeaderMapImpl Map(File.getBuffer(), NeedsSwap); + + // The string for "b" runs to the end of File. Check that the prefix + // ("bxxxx...") is detected as truncated, and an empty string is returned. + SmallString<24> DestPath; + ASSERT_EQ("", Map.lookupFilename("a", DestPath)); +} + +} // end namespace diff --git a/unittests/Lex/LexerTest.cpp b/unittests/Lex/LexerTest.cpp index 42f8eb5a5c7f8..204601818152a 100644 --- a/unittests/Lex/LexerTest.cpp +++ b/unittests/Lex/LexerTest.cpp @@ -22,7 +22,6 @@ #include "clang/Lex/PreprocessorOptions.h" #include "gtest/gtest.h" -using namespace llvm; using namespace clang; namespace { @@ -59,9 +58,9 @@ protected: Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts); } - std::vector<Token> CheckLex(StringRef Source, - ArrayRef<tok::TokenKind> ExpectedTokens) { - std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(Source); + std::vector<Token> Lex(StringRef Source) { + std::unique_ptr<llvm::MemoryBuffer> Buf = + llvm::MemoryBuffer::getMemBuffer(Source); SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); VoidModuleLoader ModLoader; @@ -82,6 +81,12 @@ protected: toks.push_back(tok); } + return toks; + } + + std::vector<Token> CheckLex(StringRef Source, + ArrayRef<tok::TokenKind> ExpectedTokens) { + auto toks = Lex(Source); EXPECT_EQ(ExpectedTokens.size(), toks.size()); for (unsigned i = 0, e = ExpectedTokens.size(); i != e; ++i) { EXPECT_EQ(ExpectedTokens[i], toks[i].getKind()); @@ -358,4 +363,21 @@ TEST_F(LexerTest, LexAPI) { EXPECT_EQ("N", Lexer::getImmediateMacroName(idLoc4, SourceMgr, LangOpts)); } +TEST_F(LexerTest, DontMergeMacroArgsFromDifferentMacroFiles) { + std::vector<Token> toks = + Lex("#define helper1 0\n" + "void helper2(const char *, ...);\n" + "#define M1(a, ...) helper2(a, ##__VA_ARGS__)\n" + "#define M2(a, ...) M1(a, helper1, ##__VA_ARGS__)\n" + "void f1() { M2(\"a\", \"b\"); }"); + + // Check the file corresponding to the "helper1" macro arg in M2. + // + // The lexer used to report its size as 31, meaning that the end of the + // expansion would be on the *next line* (just past `M2("a", "b")`). Make + // sure that we get the correct end location (the comma after "helper1"). + SourceLocation helper1ArgLoc = toks[20].getLocation(); + EXPECT_EQ(SourceMgr.getFileIDSize(SourceMgr.getFileID(helper1ArgLoc)), 8U); +} + } // anonymous namespace diff --git a/unittests/Lex/Makefile b/unittests/Lex/Makefile deleted file mode 100644 index 071d01c8b567b..0000000000000 --- a/unittests/Lex/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- unittests/Lex/Makefile ------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL = ../.. -TESTNAME = Lex -LINK_COMPONENTS := mcparser support mc bitreader -USEDLIBS = clangParse.a clangSema.a clangAnalysis.a clangEdit.a \ - clangSerialization.a clangAST.a clangLex.a clangBasic.a - -include $(CLANG_LEVEL)/unittests/Makefile diff --git a/unittests/Lex/PPConditionalDirectiveRecordTest.cpp b/unittests/Lex/PPConditionalDirectiveRecordTest.cpp index 9345fc2390266..bceeac57ea618 100644 --- a/unittests/Lex/PPConditionalDirectiveRecordTest.cpp +++ b/unittests/Lex/PPConditionalDirectiveRecordTest.cpp @@ -22,7 +22,6 @@ #include "clang/Lex/PreprocessorOptions.h" #include "gtest/gtest.h" -using namespace llvm; using namespace clang; namespace { @@ -89,7 +88,8 @@ TEST_F(PPConditionalDirectiveRecordTest, PPRecAPI) { "#endif\n" "9\n"; - std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(source); + std::unique_ptr<llvm::MemoryBuffer> Buf = + llvm::MemoryBuffer::getMemBuffer(source); SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); VoidModuleLoader ModLoader; |