include-what-you-use/more_tests/iwyu_lexer_utils_test.cc

278 lines
10 KiB
C++

//===--- iwyu_lexer_utils_test.cc - test iwyu_lexer_utils.{cc,h} ----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// Tests for the iwyu_lexer_utils module. In addition, provides sample
// code for using Clang's Lexer.
#include "iwyu_lexer_utils.h"
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include "base/logging.h"
#include "iwyu_globals.h"
#include "testing/base/public/gunit.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Token.h"
using clang::LangOptions;
using clang::Lexer;
using clang::SourceLocation;
using clang::SourceRange;
using clang::Token;
namespace iwyu = include_what_you_use;
using iwyu::CharacterDataGetterInterface;
using iwyu::FindArgumentsToDefined;
namespace {
// Hack. Work around the fact that only SourceManagers can create
// non-trivial SourceLocations.
SourceLocation CreateSourceLocationFromOffset(SourceLocation begin_loc,
unsigned offset) {
return SourceLocation::getFromRawEncoding(begin_loc.getRawEncoding()
+ offset);
}
class StringCharacterDataGetter : public CharacterDataGetterInterface {
public:
StringCharacterDataGetter(const string& str)
: str_("unused" + str) {
}
virtual const char* GetCharacterData(SourceLocation loc) const {
unsigned offset = loc.getRawEncoding();
CHECK_LE(offset, str_.size());
return str_.c_str() + offset;
}
SourceLocation BeginningOfString() {
// Returns an index into str that skips over the "unused" set in the ctor.
return CreateSourceLocationFromOffset(SourceLocation(), strlen("unused"));
}
private:
string str_;
};
TEST(LexerTest, ClangLexer) {
// Not so much a test as an example of how to use the Lexer.
const char text[] = "#if defined(FOO)";
Lexer lexer(SourceLocation(), LangOptions(), text, text, text + strlen(text));
Token token;
while (!lexer.LexFromRawLexer(token)) {
printf("Token: %s at %u length %u: \"%s\"\n",
token.getName(), token.getLocation().getRawEncoding(),
token.getLength(),
string(text + token.getLocation().getRawEncoding(),
token.getLength()).c_str());
}
}
// Common test code for testing FindArgumentsToDefined. The symbols
// should be the arguments to defined() in order.
void TestFindArgumentsToDefinedWithText(const string& text,
const vector<string>& symbols) {
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation end_loc =
CreateSourceLocationFromOffset(begin_loc, text.size());
vector<Token> defined_tokens = FindArgumentsToDefined(
SourceRange(begin_loc, end_loc), data_getter);
EXPECT_EQ(symbols.size(), defined_tokens.size());
for (int i = 0; i < symbols.size(); ++i) {
const string& symbol = symbols[i];
const Token& token = defined_tokens[i];
EXPECT_EQ(clang::tok::raw_identifier, token.getKind());
SourceLocation expected_loc =
CreateSourceLocationFromOffset(begin_loc, text.find(symbol));
EXPECT_EQ(expected_loc, token.getLocation());
EXPECT_EQ(symbol.size(), token.getLength());
EXPECT_EQ(symbol, GetTokenText(token, data_getter));
}
}
TEST(FindArgumentsToDefined, InParentheses) {
vector<string> symbols;
symbols.push_back("FOO");
TestFindArgumentsToDefinedWithText("#if defined(FOO)\n", symbols);
}
TEST(FindArgumentsToDefined, NoParentheses) {
vector<string> symbols;
symbols.push_back("FOO");
TestFindArgumentsToDefinedWithText("#if defined FOO\n", symbols);
}
TEST(FindArgumentsToDefined, MultipleArgs) {
vector<string> symbols;
symbols.push_back("FOO");
symbols.push_back("BAR");
symbols.push_back("BAZ");
TestFindArgumentsToDefinedWithText(
"#if defined FOO || defined(BAR) || !defined(BAZ)\n", symbols);
}
TEST(GetSourceTextUntilEndOfLine, FullLine) {
const char text[] = "This is the full line.\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
EXPECT_EQ("This is the full line.",
GetSourceTextUntilEndOfLine(begin_loc, data_getter));
}
TEST(GetSourceTextUntilEndOfLine, MultipleLines) {
const char text[] = "This is the full line.\nThis line should be ignored.\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
EXPECT_EQ("This is the full line.",
GetSourceTextUntilEndOfLine(begin_loc, data_getter));
}
TEST(GetSourceTextUntilEndOfLine, PartialLine) {
const char text[] = "This is the full line.\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation middle_loc = CreateSourceLocationFromOffset(begin_loc, 5);
EXPECT_EQ("is the full line.",
GetSourceTextUntilEndOfLine(middle_loc, data_getter));
}
TEST(GetSourceTextUntilEndOfLine, MiddleLine) {
const char text[] = "This is the full line.\nThis is the winning line.\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation middle_loc = CreateSourceLocationFromOffset(begin_loc, 35);
EXPECT_EQ("winning line.",
GetSourceTextUntilEndOfLine(middle_loc, data_getter));
}
TEST(GetSourceTextUntilEndOfLine, NoNewline) {
const char text[] = "This is the full line.";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
EXPECT_EQ("This is the full line.",
GetSourceTextUntilEndOfLine(begin_loc, data_getter));
}
TEST(GetLocationAfter, FullLine) {
const char text[] = "This is the full line.\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation after_loc = GetLocationAfter(begin_loc, "is the", data_getter);
EXPECT_TRUE(after_loc.isValid());
// We can't explore after_loc directly (it's opaque), so we use
// GetSourceTextUntilEndOfLine as a proxy.
EXPECT_EQ(" full line.", GetSourceTextUntilEndOfLine(after_loc, data_getter));
}
TEST(GetLocationAfter, FirstOfManyOccurrences) {
const char text[] = "This is the full line.\nThis is the full line too.\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation after_loc = GetLocationAfter(begin_loc, "is the", data_getter);
EXPECT_TRUE(after_loc.isValid());
EXPECT_EQ(" full line.", GetSourceTextUntilEndOfLine(after_loc, data_getter));
}
TEST(GetLocationAfter, SecondOfManyOccurrences) {
const char text[] = "This is the full line.\nThis is the full line too.\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation after_loc = GetLocationAfter(begin_loc, "is the", data_getter);
EXPECT_TRUE(after_loc.isValid());
after_loc = GetLocationAfter(after_loc, "is the", data_getter);
EXPECT_TRUE(after_loc.isValid());
EXPECT_EQ(" full line too.",
GetSourceTextUntilEndOfLine(after_loc, data_getter));
}
TEST(GetLocationAfter, NeedleNotFound) {
const char text[] = "This is the full line.\nThis is the full line too.\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation after_loc = GetLocationAfter(begin_loc, "isthe", data_getter);
EXPECT_FALSE(after_loc.isValid());
}
TEST(GetLocationAfter, NeedleNotFoundTwice) {
const char text[] = "This is the full line.\nThis is the full line too.\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation after_loc = GetLocationAfter(begin_loc, "line.", data_getter);
EXPECT_TRUE(after_loc.isValid());
after_loc = GetLocationAfter(after_loc, "line.", data_getter);
EXPECT_FALSE(after_loc.isValid());
}
TEST(GetLocationAfter, EmptyNeedle) {
const char text[] = "This is the full line.";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation after_loc = GetLocationAfter(begin_loc, "", data_getter);
EXPECT_TRUE(after_loc.isValid());
EXPECT_EQ("This is the full line.",
GetSourceTextUntilEndOfLine(after_loc, data_getter));
}
TEST(GetLocationAfter, BeginAfterStartOfText) {
const char text[] = "This is the full line. This is the second 'this'.";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
begin_loc = CreateSourceLocationFromOffset(begin_loc, 1);
SourceLocation after_loc = GetLocationAfter(begin_loc, "This", data_getter);
EXPECT_TRUE(after_loc.isValid());
EXPECT_EQ(" is the second 'this'.",
GetSourceTextUntilEndOfLine(after_loc, data_getter));
}
TEST(GetIncludeNameAsTyped, SystemInclude) {
const char text[] = "#include <stdio.h>\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation inc_loc = CreateSourceLocationFromOffset(begin_loc, 9);
EXPECT_EQ("<stdio.h>",
GetIncludeNameAsTyped(inc_loc, data_getter));
}
TEST(GetIncludeNameAsTyped, NonsysytemInclude) {
const char text[] = "#include \"ads/util.h\"\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation inc_loc = CreateSourceLocationFromOffset(begin_loc, 9);
EXPECT_EQ("\"ads/util.h\"",
GetIncludeNameAsTyped(inc_loc, data_getter));
}
TEST(GetIncludeNameAsTyped, WithComments) {
const char text[] = "#include <stdio.h> // for printf\n";
StringCharacterDataGetter data_getter(text);
SourceLocation begin_loc = data_getter.BeginningOfString();
SourceLocation inc_loc = CreateSourceLocationFromOffset(begin_loc, 9);
EXPECT_EQ("<stdio.h>",
GetIncludeNameAsTyped(inc_loc, data_getter));
}
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
include_what_you_use::InitGlobalsAndFlagsForTesting();
return RUN_ALL_TESTS();
}