data: Saving TokenLocations in Storage

This change adds structures for saving TokenLocations in the Storage and improves the location derival in the
ASTVisitor:
* TokenLocation saves an Id of a Token. For each Token two TokenLocations are created for both start- and endpoint. They
  keep a reference to each other and know whether they are start or end.
* TokenLocationLine saves all TokenLocations in a line and the lineNumber.
* TokenLocationFile saves all TokenLocationLines of a specific file and the filePath.
* TokenLocationCollection saves multiple TokenLocationFiles.
* TokenLocation can derive it's lineNumber and filePath through references to first TokenLocationLine and from there to
  TokenLocationFile.
This commit is contained in:
Eberhard Graether
2014-07-01 10:49:12 +02:00
parent e3cf57e2ec
commit cf6d17584f
27 changed files with 1109 additions and 243 deletions
+1
View File
@@ -11,6 +11,7 @@ add_files(
TestSuiteFixture.cpp
TestSuiteFixture.h
TextAccessTestSuite.h
TokenLocationCollectionTestSuite.h
UtilityStringTestSuite.h
Vector2TestSuite.h
)
+57 -44
View File
@@ -1,5 +1,7 @@
#include "cxxtest/TestSuite.h"
#include <sstream>
#include "data/parser/cxx/CxxParser.h"
#include "data/parser/ParseLocation.h"
#include "data/parser/ParserClient.h"
@@ -21,7 +23,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->classes.size(), 1);
TS_ASSERT_EQUALS(client->classes[0], "A");
TS_ASSERT_EQUALS(client->classes[0], "A <1:1 3:1>");
}
void test_cxx_parser_finds_global_class_forward_declaration()
@@ -34,7 +36,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->classes.size(), 1);
TS_ASSERT_EQUALS(client->classes[0], "A");
TS_ASSERT_EQUALS(client->classes[0], "A <1:1 1:7>");
}
void test_cxx_parser_finds_nested_class_definition() // TODO: test different access types here
@@ -51,8 +53,8 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->classes.size(), 2);
TS_ASSERT_EQUALS(client->classes[0], "A");
TS_ASSERT_EQUALS(client->classes[1], "public A::B");
TS_ASSERT_EQUALS(client->classes[0], "A <1:1 5:1>");
TS_ASSERT_EQUALS(client->classes[1], "public A::B <4:2 4:8>");
}
void test_cxx_parser_finds_class_definition_in_namespace()
@@ -68,7 +70,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->classes.size(), 1);
TS_ASSERT_EQUALS(client->classes[0], "a::B");
TS_ASSERT_EQUALS(client->classes[0], "a::B <3:2 3:8>");
}
void test_cxx_parser_finds_global_struct_definition()
@@ -83,7 +85,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->structs.size(), 1);
TS_ASSERT_EQUALS(client->structs[0], "A");
TS_ASSERT_EQUALS(client->structs[0], "A <1:1 3:1>");
}
void test_cxx_parser_finds_global_struct_forward_declaration()
@@ -96,7 +98,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->structs.size(), 1);
TS_ASSERT_EQUALS(client->structs[0], "A");
TS_ASSERT_EQUALS(client->structs[0], "A <1:1 1:8>");
}
void test_cxx_parser_finds_struct_definition_in_class()
@@ -114,7 +116,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->structs.size(), 1);
TS_ASSERT_EQUALS(client->structs[0], "private A::B");
TS_ASSERT_EQUALS(client->structs[0], "private A::B <3:2 5:2>");
}
void test_cxx_parser_finds_struct_definition_in_namespace()
@@ -132,7 +134,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->structs.size(), 1);
TS_ASSERT_EQUALS(client->structs[0], "A::B");
TS_ASSERT_EQUALS(client->structs[0], "A::B <3:2 5:2>");
}
void test_cxx_parser_finds_variable_definitions_in_global_scope()
@@ -147,8 +149,8 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->globals.size(), 2);
TS_ASSERT_EQUALS(client->globals[0], "int x");
TS_ASSERT_EQUALS(client->globals[1], "A * b");
TS_ASSERT_EQUALS(client->globals[0], "int x <1:1 1:5>");
TS_ASSERT_EQUALS(client->globals[1], "A * b <3:1 3:4>");
}
void test_cxx_parser_finds_variable_definitions_in_namespace_scope()
@@ -166,8 +168,8 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->globals.size(), 2);
TS_ASSERT_EQUALS(client->globals[0], "int n::x");
TS_ASSERT_EQUALS(client->globals[1], "n::A * n::b");
TS_ASSERT_EQUALS(client->globals[0], "int n::x <2:2 2:6>");
TS_ASSERT_EQUALS(client->globals[1], "n::A * n::b <4:2 4:5>");
}
void test_cxx_parser_finds_field_in_nested_class()
@@ -188,7 +190,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->fields.size(), 1);
TS_ASSERT_EQUALS(client->fields[0], "private static const int B::C::amount");
TS_ASSERT_EQUALS(client->fields[0], "private static const int B::C::amount <7:3 7:20>");
}
void test_cxx_parser_finds_field_in_global_class()
@@ -199,15 +201,15 @@ public:
"class B\n"
"{\n"
"public:\n"
" B() : count(0) {};"
" B() : count(0) {};\n"
"private:\n"
" const int count;"
" const int count;\n"
"};\n";
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->fields.size(), 1);
TS_ASSERT_EQUALS(client->fields[0], "private const int B::count");
TS_ASSERT_EQUALS(client->fields[0], "private const int B::count <6:2 6:12>");
}
void test_cxx_parser_finds_function_in_global_namespace()
@@ -223,7 +225,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->functions.size(), 1);
TS_ASSERT_EQUALS(client->functions[0], "int ceil(float a)");
TS_ASSERT_EQUALS(client->functions[0], "int ceil(float a) <1:1 4:1>");
}
void test_cxx_parser_finds_function_in_anonymous_namespace()
@@ -239,7 +241,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->functions.size(), 1);
TS_ASSERT_EQUALS(client->functions[0], "int (anonymous namespace)::sum(int a, int b)");
TS_ASSERT_EQUALS(client->functions[0], "int (anonymous namespace)::sum(int a, int b) <3:2 3:22>");
}
void test_cxx_parser_finds_method_declaration()
@@ -256,7 +258,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->methods.size(), 1);
TS_ASSERT_EQUALS(client->methods[0], "public void B::B()");
TS_ASSERT_EQUALS(client->methods[0], "public void B::B() <4:2 4:4>");
}
void test_cxx_parser_finds_method_declaration_and_definition()
@@ -276,8 +278,8 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->methods.size(), 2);
TS_ASSERT_EQUALS(client->methods[0], "public void B::B()");
TS_ASSERT_EQUALS(client->methods[1], "public void B::B()");
TS_ASSERT_EQUALS(client->methods[0], "public void B::B() <4:2 4:4>");
TS_ASSERT_EQUALS(client->methods[1], "public void B::B() <6:1 8:1>");
}
void test_cxx_parser_finds_pure_virtual_method()
@@ -294,7 +296,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->methods.size(), 1);
TS_ASSERT_EQUALS(client->methods[0], "protected pure virtual void B::process()");
TS_ASSERT_EQUALS(client->methods[0], "protected pure virtual void B::process() <4:2 4:27>");
}
void test_cxx_parser_finds_method_declared_in_nested_class()
@@ -313,7 +315,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->methods.size(), 1);
TS_ASSERT_EQUALS(client->methods[0], "private _Bool B::C::isGreat() const");
TS_ASSERT_EQUALS(client->methods[0], "private _Bool B::C::isGreat() const <5:3 5:18>");
}
void test_cxx_parser_finds_named_namespace()
@@ -322,13 +324,13 @@ public:
CxxParser parser(client);
std::string text =
"namespace A\n"
"{"
"{\n"
"}\n";
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->namespaces.size(), 1);
TS_ASSERT_EQUALS(client->namespaces[0], "A");
TS_ASSERT_EQUALS(client->namespaces[0], "A <1:1 3:1>");
}
void test_cxx_parser_finds_anonymous_namespace()
@@ -343,7 +345,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->namespaces.size(), 1);
TS_ASSERT_EQUALS(client->namespaces[0], "(anonymous)");
TS_ASSERT_EQUALS(client->namespaces[0], "(anonymous) <1:1 3:1>");
}
void test_cxx_parser_finds_enum_defined_in_global_namespace()
@@ -358,7 +360,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->enums.size(), 1);
TS_ASSERT_EQUALS(client->enums[0], "E");
TS_ASSERT_EQUALS(client->enums[0], "E <1:1 3:1>");
}
void test_cxx_parser_finds_enum_defined_in_class()
@@ -377,7 +379,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->enums.size(), 1);
TS_ASSERT_EQUALS(client->enums[0], "public B::Z");
TS_ASSERT_EQUALS(client->enums[0], "public B::Z <4:2 6:2>");
}
void test_cxx_parser_finds_enum_defined_in_namespace()
@@ -395,7 +397,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->enums.size(), 1);
TS_ASSERT_EQUALS(client->enums[0], "n::Z");
TS_ASSERT_EQUALS(client->enums[0], "n::Z <3:2 5:2>");
}
void test_cxx_parser_finds_enum_field_in_global_enum()
@@ -411,7 +413,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->enumFields.size(), 1);
TS_ASSERT_EQUALS(client->enumFields[0], "E::P");
TS_ASSERT_EQUALS(client->enumFields[0], "E::P <3:2 3:2>");
}
void test_cxx_parser_finds_typedef_in_global_namespace()
@@ -423,7 +425,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->typedefs.size(), 1);
TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> uint");
TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> uint <1:1 1:22>");
}
void test_cxx_parser_finds_typedef_in_named_namespace()
@@ -439,7 +441,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->typedefs.size(), 1);
TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> test::uint");
TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> test::uint <3:2 3:23>");
}
void test_cxx_parser_finds_typedef_in_anonymous_namespace()
@@ -455,7 +457,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->typedefs.size(), 1);
TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> (anonymous namespace)::uint");
TS_ASSERT_EQUALS(client->typedefs[0], "unsigned int -> (anonymous namespace)::uint <3:2 3:23>");
}
void test_cxx_parser_finds_typedef_that_uses_type_defined_in_named_namespace()
@@ -472,7 +474,7 @@ public:
parser.parseFile(TextAccess::createFromString(text));
TS_ASSERT_EQUALS(client->typedefs.size(), 1);
TS_ASSERT_EQUALS(client->typedefs[0], "test::TestStruct -> globalTestStruct");
TS_ASSERT_EQUALS(client->typedefs[0], "test::TestStruct -> globalTestStruct <5:1 5:26>");
}
void test_cxx_parser_parses_multiple_files()
@@ -506,27 +508,28 @@ private:
AccessType access
)
{
typedefs.push_back(addAccessPrefix(underlyingFullName + " -> " + fullName, access));
std::string str = addAccessPrefix(underlyingFullName + " -> " + fullName, access);
typedefs.push_back(addLocationSuffix(str, location));
}
virtual void onClassParsed(const ParseLocation& location, const std::string& fullName, AccessType access)
{
classes.push_back(addAccessPrefix(fullName, access));
classes.push_back(addLocationSuffix(addAccessPrefix(fullName, access), location));
}
virtual void onStructParsed(const ParseLocation& location, const std::string& fullName, AccessType access)
{
structs.push_back(addAccessPrefix(fullName, access));
structs.push_back(addLocationSuffix(addAccessPrefix(fullName, access), location));
}
virtual void onGlobalVariableParsed(const ParseLocation& location, const ParseVariable& variable)
{
globals.push_back(variableStr(variable));
globals.push_back(addLocationSuffix(variableStr(variable), location));
}
virtual void onFieldParsed(const ParseLocation& location, const ParseVariable& variable, AccessType access)
{
fields.push_back(addAccessPrefix(variableStr(variable), access));
fields.push_back(addLocationSuffix(addAccessPrefix(variableStr(variable), access), location));
}
virtual void onFunctionParsed(
@@ -534,7 +537,8 @@ private:
const std::vector<ParseVariable>& parameters
)
{
functions.push_back(returnTypeName + " " + fullName + parameterStr(parameters));
std::string str = returnTypeName + " " + fullName + parameterStr(parameters);
functions.push_back(addLocationSuffix(str, location));
}
virtual void onMethodParsed(
@@ -546,22 +550,23 @@ private:
std::string str = returnTypeName + " " + fullName + parameterStr(parameters);
str = addStaticPrefix(addAbstractionPrefix(str, abstraction), isStatic);
str = addConstPrefix(addAccessPrefix(str, access), isConst, false);
str = addLocationSuffix(str, location);
methods.push_back(str);
}
virtual void onNamespaceParsed(const ParseLocation& location, const std::string& fullName)
{
namespaces.push_back(fullName);
namespaces.push_back(addLocationSuffix(fullName, location));
}
virtual void onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access)
{
enums.push_back(addAccessPrefix(fullName, access));
enums.push_back(addLocationSuffix(addAccessPrefix(fullName, access), location));
}
virtual void onEnumFieldParsed(const ParseLocation& location, const std::string& fullName)
{
enumFields.push_back(fullName);
enumFields.push_back(addLocationSuffix(fullName, location));
}
std::vector<std::string> typedefs;
@@ -641,5 +646,13 @@ private:
}
return str + ")";
}
std::string addLocationSuffix(const std::string& str, const ParseLocation& location)
{
std::stringstream ss;
ss << str << " <" << location.startLineNumber << ":" << location.startColumnNumber << " ";
ss << location.endLineNumber << ":" << location.endColumnNumber << ">";
return ss.str();
}
};
};
+162
View File
@@ -0,0 +1,162 @@
#include "cxxtest/TestSuite.h"
#include "data/location/TokenLocation.h"
#include "data/location/TokenLocationCollection.h"
#include "data/location/TokenLocationFile.h"
#include "data/location/TokenLocationLine.h"
class TokenLocationCollectionTestSuite : public CxxTest::TestSuite
{
public:
void test_token_locations_get_created_with_other_end()
{
TokenLocationCollection collection;
TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 4, 5);
TS_ASSERT(a);
TS_ASSERT(a->isStartTokenLocation());
TS_ASSERT(!a->isEndTokenLocation());
TokenLocation* b = a->getOtherTokenLocation();
TS_ASSERT(b);
TS_ASSERT(!b->isStartTokenLocation());
TS_ASSERT(b->isEndTokenLocation());
TS_ASSERT_EQUALS(a, b->getOtherTokenLocation());
TS_ASSERT_EQUALS(a, b->getStartTokenLocation());
TS_ASSERT_EQUALS(a, a->getStartTokenLocation());
TS_ASSERT_EQUALS(b, a->getEndTokenLocation());
TS_ASSERT_EQUALS(b, b->getEndTokenLocation());
}
void test_token_locations_do_not_get_created_with_wrong_input()
{
TokenLocationCollection collection;
TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 2, 1);
TokenLocation* b = collection.addTokenLocation(1, "file.c", 4, 1, 1, 10);
TS_ASSERT(!a);
TS_ASSERT(!b);
}
void test_token_locations_get_unique_id_but_both_ends_have_the_same()
{
TokenLocationCollection collection;
TokenLocation* a = collection.addTokenLocation(1, "file.c", 1, 1, 1, 1);
TokenLocation* b = collection.addTokenLocation(2, "file.c", 1, 1, 1, 1);
TokenLocation* c = collection.addTokenLocation(3, "file.c", 1, 1, 1, 1);
TS_ASSERT_EQUALS(1, collection.getTokenLocationFileCount());
TS_ASSERT_EQUALS(3, collection.getTokenLocationCount());
TS_ASSERT_DIFFERS(a->getId(), b->getId());
TS_ASSERT_DIFFERS(b->getId(), c->getId());
TS_ASSERT_DIFFERS(c->getId(), a->getId());
TS_ASSERT_EQUALS(a->getId(), a->getOtherTokenLocation()->getId());
TS_ASSERT_EQUALS(b->getId(), b->getOtherTokenLocation()->getId());
TS_ASSERT_EQUALS(c->getId(), c->getOtherTokenLocation()->getId());
}
void test_token_locations_have_right_file_path_line_column_and_token_id()
{
TokenLocationCollection collection;
TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 4, 5);
TS_ASSERT_EQUALS(1, a->getTokenId());
TS_ASSERT_EQUALS(2, a->getLineNumber());
TS_ASSERT_EQUALS(3, a->getColumnNumber());
TS_ASSERT_EQUALS(4, a->getOtherTokenLocation()->getLineNumber());
TS_ASSERT_EQUALS(5, a->getOtherTokenLocation()->getColumnNumber());
TS_ASSERT_EQUALS("file.c", a->getFilePath());
}
void test_finding_token_locations_by_id()
{
TokenLocationCollection collection;
TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 4, 5);
TokenLocation* b = collection.addTokenLocation(6, "file.c", 7, 8, 9, 10);
TS_ASSERT_EQUALS(a, collection.findTokenLocationById(a->getId()));
TS_ASSERT_EQUALS(b, collection.findTokenLocationById(b->getId()));
}
void test_removing_token_locations()
{
TokenLocationCollection collection;
TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 4, 5);
TokenLocation* b = collection.addTokenLocation(1, "file.c", 3, 3, 4, 5);
TokenLocation* c = collection.addTokenLocation(1, "file.c", 1, 3, 5, 5);
TokenLocation* d = collection.addTokenLocation(1, "file2.c", 1, 3, 5, 5);
TS_ASSERT_EQUALS(2, collection.getTokenLocationFileCount());
TS_ASSERT_EQUALS(4, collection.getTokenLocationCount());
TS_ASSERT_EQUALS(5, c->getTokenLocationFile()->getTokenLocationLineCount());
Id ida = a->getId();
Id idb = b->getId();
Id idc = c->getId();
collection.removeTokenLocation(a);
collection.removeTokenLocation(b->getOtherTokenLocation());
collection.removeTokenLocation(d);
TS_ASSERT(!collection.findTokenLocationById(ida));
TS_ASSERT(!collection.findTokenLocationById(idb));
TS_ASSERT_EQUALS(c, collection.findTokenLocationById(idc));
TS_ASSERT_EQUALS(1, collection.getTokenLocationFileCount());
TS_ASSERT_EQUALS(1, collection.getTokenLocationCount());
TS_ASSERT_EQUALS(2, c->getTokenLocationFile()->getTokenLocationLineCount());
}
void test_creating_plain_copy_of_all_locations_in_line_range()
{
TokenLocationCollection collection;
TokenLocation* a = collection.addTokenLocation(1, "file.c", 2, 3, 4, 5);
TokenLocation* b = collection.addTokenLocation(1, "file.c", 3, 3, 4, 5);
TokenLocation* c = collection.addTokenLocation(1, "file.c", 1, 3, 5, 5);
TokenLocation* d = collection.addTokenLocation(1, "file.c", 1, 5, 4, 5);
Id ida = a->getId();
Id idb = b->getId();
Id idc = c->getId();
Id idd = d->getId();
unsigned int fromLine = 2;
unsigned int toLine = 4;
TokenLocationCollection copy;
TokenLocation* x = collection.findTokenLocationById(ida);
x->getTokenLocationFile()->forEachTokenLocationLine([&copy, fromLine, toLine](TokenLocationLine* line)
{
unsigned int l = line->getLineNumber();
if (l >= fromLine && l <= toLine)
{
line->forEachTokenLocation([&copy](TokenLocation* location)
{
copy.addTokenLocationAsPlainCopy(location);
});
}
});
TS_ASSERT_EQUALS(1, copy.getTokenLocationFileCount());
TS_ASSERT_EQUALS(3, copy.getTokenLocationCount());
TS_ASSERT(copy.findTokenLocationById(ida));
TS_ASSERT(copy.findTokenLocationById(idb));
TS_ASSERT(!copy.findTokenLocationById(idc));
TS_ASSERT(copy.findTokenLocationById(idd));
TS_ASSERT_DIFFERS(a, copy.findTokenLocationById(ida));
TS_ASSERT_DIFFERS(d, copy.findTokenLocationById(idd));
TS_ASSERT(copy.findTokenLocationById(ida)->getStartTokenLocation());
TS_ASSERT(copy.findTokenLocationById(ida)->getEndTokenLocation());
TS_ASSERT(!copy.findTokenLocationById(idd)->getStartTokenLocation());
TS_ASSERT(copy.findTokenLocationById(idd)->getEndTokenLocation());
}
};