ClangRefactoring: Improve follow symbol and usage

Change-Id: Idb42010443e4560489ef067e54d05b4e567598e9
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2019-08-22 16:10:38 +02:00
parent c174eb378a
commit 9f805b7e8a
19 changed files with 324 additions and 53 deletions

View File

@@ -201,15 +201,14 @@ struct HighlightingTypes {
MixinHighlightingTypes mixinHighlightingTypes; MixinHighlightingTypes mixinHighlightingTypes;
}; };
enum class SourceLocationKind : uchar enum class SourceLocationKind : uchar {
{ Definition = 1,
None = 0,
Declaration, Declaration,
DeclarationReference, DeclarationReference,
Definition,
MacroDefinition = 128, MacroDefinition = 128,
MacroUndefinition,
MacroUsage, MacroUsage,
MacroUndefinition None = 255,
}; };
enum class SymbolKind : uchar enum class SymbolKind : uchar

View File

@@ -82,13 +82,15 @@ public:
Sqlite::Table table; Sqlite::Table table;
table.setUseIfNotExists(true); table.setUseIfNotExists(true);
table.setName("locations"); table.setName("locations");
table.addColumn("symbolId", Sqlite::ColumnType::Integer); const Sqlite::Column &symbolIdColumn = table.addColumn("symbolId",
Sqlite::ColumnType::Integer);
const Sqlite::Column &lineColumn = table.addColumn("line", Sqlite::ColumnType::Integer); const Sqlite::Column &lineColumn = table.addColumn("line", Sqlite::ColumnType::Integer);
const Sqlite::Column &columnColumn = table.addColumn("column", Sqlite::ColumnType::Integer); const Sqlite::Column &columnColumn = table.addColumn("column", Sqlite::ColumnType::Integer);
const Sqlite::Column &sourceIdColumn = table.addColumn("sourceId", Sqlite::ColumnType::Integer); const Sqlite::Column &sourceIdColumn = table.addColumn("sourceId", Sqlite::ColumnType::Integer);
const Sqlite::Column &locationKindColumn = table.addColumn("locationKind", Sqlite::ColumnType::Integer); const Sqlite::Column &locationKindColumn = table.addColumn("locationKind", Sqlite::ColumnType::Integer);
table.addUniqueIndex({sourceIdColumn, lineColumn, columnColumn}); table.addUniqueIndex({sourceIdColumn, lineColumn, columnColumn});
table.addIndex({sourceIdColumn, locationKindColumn}); table.addIndex({sourceIdColumn, locationKindColumn});
table.addIndex({symbolIdColumn});
table.initialize(database); table.initialize(database);
} }

View File

@@ -47,7 +47,20 @@ public:
ReadStatement selectSourceUsagesForSymbolLocation{ ReadStatement selectSourceUsagesForSymbolLocation{
"SELECT directoryPath || '/' || sourceName, line, column " "SELECT directoryPath || '/' || sourceName, line, column "
"FROM locations NATURAL JOIN sources NATURAL JOIN directories " "FROM locations NATURAL JOIN sources NATURAL JOIN directories "
"WHERE symbolId = (SELECT symbolId FROM locations WHERE sourceId=? AND line=? AND column=?)", "WHERE symbolId = (SELECT symbolId FROM locations WHERE sourceId=? AND line=? AND "
"column=?)",
database};
ReadStatement selectSourceUsagesOrderedForSymbolLocation{
"SELECT directoryPath || '/' || sourceName, line, column "
"FROM locations NATURAL JOIN sources NATURAL JOIN directories "
"WHERE symbolId = (SELECT symbolId FROM locations WHERE sourceId=? AND line=? AND "
"column=?) ORDER BY locationKind LIMIT 2",
database};
ReadStatement selectSourceUsagesByLocationKindForSymbolLocation{
"SELECT directoryPath || '/' || sourceName, line, column "
"FROM locations NATURAL JOIN sources NATURAL JOIN directories "
"WHERE symbolId = (SELECT symbolId FROM locations WHERE sourceId=? AND line=? AND "
"column=?) AND locationKind = ?",
database}; database};
ReadStatement selectSymbolsForKindAndStartsWith{ ReadStatement selectSymbolsForKindAndStartsWith{
"SELECT symbolId, symbolName, signature FROM symbols WHERE symbolKind = ? AND symbolName LIKE ?", "SELECT symbolId, symbolName, signature FROM symbols WHERE symbolKind = ? AND symbolName LIKE ?",

View File

@@ -68,7 +68,8 @@ void RefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &,
CppTools::Usages RefactoringEngine::locationsAt(const CppTools::CursorInEditor &data) const CppTools::Usages RefactoringEngine::locationsAt(const CppTools::CursorInEditor &data) const
{ {
CppTools::Usages usages; if (data.cursor().isNull())
return {};
QTextCursor cursor = Utils::Text::wordStartCursor(data.cursor()); QTextCursor cursor = Utils::Text::wordStartCursor(data.cursor());
Utils::OptionalLineColumn lineColumn = Utils::Text::convertPosition(cursor.document(), Utils::OptionalLineColumn lineColumn = Utils::Text::convertPosition(cursor.document(),
@@ -78,10 +79,30 @@ CppTools::Usages RefactoringEngine::locationsAt(const CppTools::CursorInEditor &
const QByteArray filePath = data.filePath().toString().toUtf8(); const QByteArray filePath = data.filePath().toString().toUtf8();
const ClangBackEnd::FilePathId filePathId = m_filePathCache.filePathId(ClangBackEnd::FilePathView(filePath)); const ClangBackEnd::FilePathId filePathId = m_filePathCache.filePathId(ClangBackEnd::FilePathView(filePath));
usages = m_symbolQuery.sourceUsagesAt(filePathId, lineColumn->line, lineColumn->column); return m_symbolQuery.sourceUsagesAt(filePathId, lineColumn->line, lineColumn->column);
} }
return usages; return {};
}
CppTools::Usages RefactoringEngine::declarationAt(const CppTools::CursorInEditor &data) const
{
if (data.cursor().isNull())
return {};
QTextCursor cursor = Utils::Text::wordStartCursor(data.cursor());
Utils::OptionalLineColumn lineColumn = Utils::Text::convertPosition(cursor.document(),
cursor.position());
if (lineColumn) {
const QByteArray filePath = data.filePath().toString().toUtf8();
const ClangBackEnd::FilePathId filePathId = m_filePathCache.filePathId(
ClangBackEnd::FilePathView(filePath));
return m_symbolQuery.declarationsAt(filePathId, lineColumn->line, lineColumn->column);
}
return {};
} }
void RefactoringEngine::globalRename(const CppTools::CursorInEditor &data, void RefactoringEngine::globalRename(const CppTools::CursorInEditor &data,
@@ -104,16 +125,13 @@ void RefactoringEngine::globalFollowSymbol(const CppTools::CursorInEditor &data,
CppTools::SymbolFinder *, CppTools::SymbolFinder *,
bool) const bool) const
{ {
// TODO: replace that with specific followSymbol query const CppTools::Usages usages = declarationAt(data);
const CppTools::Usages usages = locationsAt(data);
CppTools::Usage usage = Utils::findOrDefault(usages, [&data](const CppTools::Usage &usage) { CppTools::Usage usage = Utils::findOrDefault(usages, [&data](const CppTools::Usage &usage) {
// We've already searched in the current file, skip it. return usage.path != data.filePath().toString();
if (usage.path == data.filePath().toString())
return false;
return true;
}); });
processLinkCallback(Link(usage.path, usage.line, usage.column)); processLinkCallback(Link(usage.path, usage.line, usage.column - 1));
} }
bool RefactoringEngine::isRefactoringEngineAvailable() const bool RefactoringEngine::isRefactoringEngineAvailable() const

View File

@@ -74,6 +74,7 @@ public:
private: private:
CppTools::Usages locationsAt(const CppTools::CursorInEditor &data) const; CppTools::Usages locationsAt(const CppTools::CursorInEditor &data) const;
CppTools::Usages declarationAt(const CppTools::CursorInEditor &data) const;
ClangBackEnd::RefactoringServerInterface &m_server; ClangBackEnd::RefactoringServerInterface &m_server;
ClangBackEnd::RefactoringClientInterface &m_client; ClangBackEnd::RefactoringClientInterface &m_client;

View File

@@ -75,6 +75,36 @@ public:
utf8Column); utf8Column);
} }
CppTools::Usages sourceUsagesAtByLocationKind(ClangBackEnd::FilePathId filePathId,
int line,
int utf8Column,
ClangBackEnd::SourceLocationKind kind) const override
{
ReadStatement &locationsStatement = m_statementFactory.selectSourceUsagesByLocationKindForSymbolLocation;
const std::size_t reserveSize = 128;
return locationsStatement.template values<CppTools::Usage, 3>(reserveSize,
filePathId.filePathId,
line,
utf8Column,
int(kind));
}
CppTools::Usages declarationsAt(ClangBackEnd::FilePathId filePathId,
int line,
int utf8Column) const override
{
ReadStatement &locationsStatement = m_statementFactory.selectSourceUsagesOrderedForSymbolLocation;
const std::size_t reserveSize = 128;
return locationsStatement.template values<CppTools::Usage, 3>(reserveSize,
filePathId.filePathId,
line,
utf8Column);
}
Symbols symbolsWithOneSymbolKinds(ClangBackEnd::SymbolKind symbolKind, Symbols symbolsWithOneSymbolKinds(ClangBackEnd::SymbolKind symbolKind,
Utils::SmallStringView searchTerm) const Utils::SmallStringView searchTerm) const
{ {

View File

@@ -46,10 +46,17 @@ public:
virtual CppTools::Usages sourceUsagesAt(ClangBackEnd::FilePathId filePathId, virtual CppTools::Usages sourceUsagesAt(ClangBackEnd::FilePathId filePathId,
int line, int line,
int utf8Column) const = 0; int utf8Column) const = 0;
virtual CppTools::Usages sourceUsagesAtByLocationKind(ClangBackEnd::FilePathId filePathId,
int line,
int utf8Column,
ClangBackEnd::SourceLocationKind) const = 0;
virtual Symbols symbols(const ClangBackEnd::SymbolKinds &symbolKinds, virtual Symbols symbols(const ClangBackEnd::SymbolKinds &symbolKinds,
Utils::SmallStringView searchTerm) const = 0; Utils::SmallStringView searchTerm) const = 0;
virtual Utils::optional<SourceLocation> locationForSymbolId(SymbolId symbolId, virtual Utils::optional<SourceLocation> locationForSymbolId(SymbolId symbolId,
ClangBackEnd::SourceLocationKind kind) const = 0; ClangBackEnd::SourceLocationKind kind) const = 0;
virtual CppTools::Usages declarationsAt(ClangBackEnd::FilePathId filePathId,
int line,
int utf8Column) const = 0;
protected: protected:
~SymbolQueryInterface() = default; ~SymbolQueryInterface() = default;

View File

@@ -52,12 +52,12 @@ Utils::SmallString symbolName(const clang::NamedDecl *declaration)
SourceLocationKind sourceLocationKind(clang::index::SymbolRoleSet roles) SourceLocationKind sourceLocationKind(clang::index::SymbolRoleSet roles)
{ {
if (hasSymbolRole(clang::index::SymbolRole::Reference, roles)) if (hasSymbolRole(clang::index::SymbolRole::Definition, roles))
return SourceLocationKind::DeclarationReference; return SourceLocationKind::Definition;
else if (hasSymbolRole(clang::index::SymbolRole::Declaration, roles)) else if (hasSymbolRole(clang::index::SymbolRole::Declaration, roles))
return SourceLocationKind::Declaration; return SourceLocationKind::Declaration;
else if (hasSymbolRole(clang::index::SymbolRole::Definition, roles)) else if (hasSymbolRole(clang::index::SymbolRole::Reference, roles))
return SourceLocationKind::Definition; return SourceLocationKind::DeclarationReference;
return SourceLocationKind::None; return SourceLocationKind::None;
} }

View File

@@ -0,0 +1,14 @@
class Class
{
Class();
Class(int);
void foo();
void bar() {}
};
void Class::foo()
{
bar();
}

View File

@@ -453,11 +453,11 @@ TEST_F(FilePathCache, GetFileIdInAfterPopulateIfEmpty)
TEST_F(FilePathCache, DontPopulateIfNotEmpty) TEST_F(FilePathCache, DontPopulateIfNotEmpty)
{ {
cacheNotFilled.filePathId("/path/to/file.cpp"); cacheNotFilled.filePathId("/path/to/file.cpp");
EXPECT_CALL(mockStorageFilled, fetchAllDirectories()).Times(0);
EXPECT_CALL(mockStorageFilled, fetchAllSources()).Times(0);
cacheNotFilled.populateIfEmpty(); cacheNotFilled.populateIfEmpty();
auto id = cacheNotFilled.filePathId("/path2/to/file.cpp");
ASSERT_FALSE(id.isValid());
} }
TEST_F(FilePathCache, GetDirectoryIdAfterPopulateIfEmpty) TEST_F(FilePathCache, GetDirectoryIdAfterPopulateIfEmpty)

View File

@@ -37,6 +37,9 @@
#include <clangcodemodelservermessages.h> #include <clangcodemodelservermessages.h>
#include <clangpathwatcher.h> #include <clangpathwatcher.h>
#include <clangrefactoringmessages.h> #include <clangrefactoringmessages.h>
#include <coreplugin/find/searchresultitem.h>
#include <coreplugin/locator/ilocatorfilter.h>
#include <cpptools/usages.h>
#include <filepath.h> #include <filepath.h>
#include <filepathcaching.h> #include <filepathcaching.h>
#include <filepathview.h> #include <filepathview.h>
@@ -46,6 +49,8 @@
#include <pchpaths.h> #include <pchpaths.h>
#include <pchtask.h> #include <pchtask.h>
#include <precompiledheadersupdatedmessage.h> #include <precompiledheadersupdatedmessage.h>
#include <projectexplorer/headerpath.h>
#include <projectexplorer/projectmacro.h>
#include <projectpartartefact.h> #include <projectpartartefact.h>
#include <projectpartentry.h> #include <projectpartentry.h>
#include <projectpartpch.h> #include <projectpartpch.h>
@@ -58,11 +63,7 @@
#include <toolchainargumentscache.h> #include <toolchainargumentscache.h>
#include <tooltipinfo.h> #include <tooltipinfo.h>
#include <usedmacro.h> #include <usedmacro.h>
#include <cpptools/usages.h> #include <utils/link.h>
#include <projectexplorer/projectmacro.h>
#include <projectexplorer/headerpath.h>
#include <coreplugin/find/searchresultitem.h>
#include <coreplugin/locator/ilocatorfilter.h>
namespace { namespace {
ClangBackEnd::FilePathCaching *filePathCache = nullptr; ClangBackEnd::FilePathCaching *filePathCache = nullptr;
@@ -178,6 +179,12 @@ std::ostream &operator<<(std::ostream &out, const LineColumn &lineColumn)
return out << "(" << lineColumn.line << ", " << lineColumn.column << ")"; return out << "(" << lineColumn.line << ", " << lineColumn.column << ")";
} }
std::ostream &operator<<(std::ostream &out, const Link &link)
{
return out << "(" << link.targetFileName << ", " << link.targetLine << ", " << link.targetColumn
<< ", " << link.linkTextStart << ", " << link.linkTextEnd << ")";
}
const char * toText(Utils::Language language) const char * toText(Utils::Language language)
{ {
using Utils::Language; using Utils::Language;

View File

@@ -81,11 +81,13 @@ std::ostream &operator<<(std::ostream &out, const HeaderPath &headerPath);
namespace Utils { namespace Utils {
class LineColumn; class LineColumn;
class SmallStringView; class SmallStringView;
class Link;
std::ostream &operator<<(std::ostream &out, const LineColumn &lineColumn); std::ostream &operator<<(std::ostream &out, const LineColumn &lineColumn);
std::ostream &operator<<(std::ostream &out, const Utils::Language &language); std::ostream &operator<<(std::ostream &out, const Utils::Language &language);
std::ostream &operator<<(std::ostream &out, const Utils::LanguageVersion &languageVersion); std::ostream &operator<<(std::ostream &out, const Utils::LanguageVersion &languageVersion);
std::ostream &operator<<(std::ostream &out, const Utils::LanguageExtension &languageExtension); std::ostream &operator<<(std::ostream &out, const Utils::LanguageExtension &languageExtension);
std::ostream &operator<<(std::ostream &out, const Link &link);
template <typename Type> template <typename Type>
std::ostream &operator<<(std::ostream &out, const Utils::optional<Type> &optional) std::ostream &operator<<(std::ostream &out, const Utils::optional<Type> &optional)

View File

@@ -46,6 +46,16 @@ MockSqliteReadStatement::values<CppTools::Usage, 3>(
return valuesReturnSourceUsages(reserveSize, sourceId, line, column); return valuesReturnSourceUsages(reserveSize, sourceId, line, column);
} }
template<>
CppTools::Usages MockSqliteReadStatement::values<CppTools::Usage, 3>(std::size_t reserveSize,
const int &sourceId,
const int &line,
const int &column,
const int &locationKind)
{
return valuesReturnSourceUsages(reserveSize, sourceId, line, column, locationKind);
}
template <> template <>
Symbols Symbols
MockSqliteReadStatement::values<Symbol, 3>( MockSqliteReadStatement::values<Symbol, 3>(

View File

@@ -78,8 +78,9 @@ public:
MOCK_METHOD4(valuesReturnSourceLocations, MOCK_METHOD4(valuesReturnSourceLocations,
SourceLocations(std::size_t, int, int, int)); SourceLocations(std::size_t, int, int, int));
MOCK_METHOD4(valuesReturnSourceUsages, MOCK_METHOD4(valuesReturnSourceUsages, CppTools::Usages(std::size_t, int, int, int));
CppTools::Usages(std::size_t, int, int, int));
MOCK_METHOD5(valuesReturnSourceUsages, CppTools::Usages(std::size_t, int, int, int, int));
MOCK_METHOD1(valuesReturnStdVectorDirectory, MOCK_METHOD1(valuesReturnStdVectorDirectory,
std::vector<Sources::Directory>(std::size_t)); std::vector<Sources::Directory>(std::size_t));
@@ -191,6 +192,13 @@ MockSqliteReadStatement::values<CppTools::Usage, 3>(
const int &line, const int &line,
const int &column); const int &column);
template<>
CppTools::Usages MockSqliteReadStatement::values<CppTools::Usage, 3>(std::size_t reserveSize,
const int &sourceId,
const int &line,
const int &column,
const int &locationKind);
template <> template <>
Symbols Symbols
MockSqliteReadStatement::values<Symbol, 3>( MockSqliteReadStatement::values<Symbol, 3>(

View File

@@ -40,4 +40,11 @@ public:
ClangRefactoring::Symbols(const ClangBackEnd::SymbolKinds &symbolKinds, Utils::SmallStringView searchTerm)); ClangRefactoring::Symbols(const ClangBackEnd::SymbolKinds &symbolKinds, Utils::SmallStringView searchTerm));
MOCK_CONST_METHOD2(locationForSymbolId, MOCK_CONST_METHOD2(locationForSymbolId,
Utils::optional<ClangRefactoring::SourceLocation>(ClangRefactoring::SymbolId symbolId, ClangBackEnd::SourceLocationKind kind)); Utils::optional<ClangRefactoring::SourceLocation>(ClangRefactoring::SymbolId symbolId, ClangBackEnd::SourceLocationKind kind));
MOCK_CONST_METHOD4(sourceUsagesAtByLocationKind,
CppTools::Usages(ClangBackEnd::FilePathId filePathId,
int line,
int utf8Column,
ClangBackEnd::SourceLocationKind));
MOCK_CONST_METHOD3(declarationsAt,
CppTools::Usages(ClangBackEnd::FilePathId filePathId, int line, int utf8Column));
}; };

View File

@@ -60,7 +60,9 @@ TEST_F(RefactoringDatabaseInitializer, AddLocationsTable)
EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS locations(symbolId INTEGER, line INTEGER, column INTEGER, sourceId INTEGER, locationKind INTEGER)"))); EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS locations(symbolId INTEGER, line INTEGER, column INTEGER, sourceId INTEGER, locationKind INTEGER)")));
EXPECT_CALL(mockDatabase, execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_locations_sourceId_line_column ON locations(sourceId, line, column)"))); EXPECT_CALL(mockDatabase, execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_locations_sourceId_line_column ON locations(sourceId, line, column)")));
EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_locations_sourceId_locationKind ON locations(sourceId, locationKind)"))); EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_locations_sourceId_locationKind ON locations(sourceId, locationKind)")));
EXPECT_CALL(mockDatabase,
execute(Eq(
"CREATE INDEX IF NOT EXISTS index_locations_symbolId ON locations(symbolId)")));
initializer.createLocationsTable(); initializer.createLocationsTable();
} }
@@ -220,6 +222,9 @@ TEST_F(RefactoringDatabaseInitializer, CreateInTheContructor)
EXPECT_CALL(mockDatabase, EXPECT_CALL(mockDatabase,
execute(Eq("CREATE INDEX IF NOT EXISTS index_locations_sourceId_locationKind ON " execute(Eq("CREATE INDEX IF NOT EXISTS index_locations_sourceId_locationKind ON "
"locations(sourceId, locationKind)"))); "locations(sourceId, locationKind)")));
EXPECT_CALL(mockDatabase,
execute(Eq(
"CREATE INDEX IF NOT EXISTS index_locations_symbolId ON locations(symbolId)")));
EXPECT_CALL(mockDatabase, EXPECT_CALL(mockDatabase,
execute(Eq("CREATE TABLE IF NOT EXISTS sources(sourceId INTEGER PRIMARY KEY, " execute(Eq("CREATE TABLE IF NOT EXISTS sources(sourceId INTEGER PRIMARY KEY, "
"directoryId INTEGER, sourceName TEXT)"))); "directoryId INTEGER, sourceName TEXT)")));

View File

@@ -65,13 +65,15 @@ protected:
commandLine = Utils::SmallStringVector( commandLine = Utils::SmallStringVector(
optionsBuilder.build(projectFile.kind, CppTools::UsePrecompiledHeaders::No)); optionsBuilder.build(projectFile.kind, CppTools::UsePrecompiledHeaders::No));
commandLine.push_back(qStringFilePath); commandLine.push_back(qStringFilePath);
ON_CALL(mockFilePathCaching, filePathId(Eq(clangBackEndFilePath))).WillByDefault(Return(12));
cursor.setPosition(11);
} }
protected: protected:
NiceMock<MockFilePathCaching> mockFilePathCaching; NiceMock<MockFilePathCaching> mockFilePathCaching;
MockRefactoringServer mockRefactoringServer; MockRefactoringServer mockRefactoringServer;
MockRefactoringClient mockRefactoringClient; MockRefactoringClient mockRefactoringClient;
MockSymbolQuery mockSymbolQuery; NiceMock<MockSymbolQuery> mockSymbolQuery;
ClangRefactoring::RefactoringEngine engine{mockRefactoringServer, ClangRefactoring::RefactoringEngine engine{mockRefactoringServer,
mockRefactoringClient, mockRefactoringClient,
mockFilePathCaching, mockFilePathCaching,
@@ -85,27 +87,96 @@ protected:
SmallStringVector commandLine; SmallStringVector commandLine;
ProjectExplorer::Project project; ProjectExplorer::Project project;
CppTools::ProjectPart::Ptr projectPart; CppTools::ProjectPart::Ptr projectPart;
CppTools::Usages usages{{"/path1", 1, 3}, {"/path2", 4, 5}};
CppTools::ProjectFile projectFile{qStringFilePath, CppTools::ProjectFile::CXXSource}; CppTools::ProjectFile projectFile{qStringFilePath, CppTools::ProjectFile::CXXSource};
}; };
TEST_F(RefactoringEngine, ExpectSourceUsagesAtInFindUsages) TEST_F(RefactoringEngine, FindUsages)
{ {
cursor.setPosition(11); ON_CALL(mockSymbolQuery, sourceUsagesAt(Eq(12), 2, 5)).WillByDefault(Return(usages));
NiceMock<MockFunction<void(const CppTools::Usages &)>> mockCallback;
EXPECT_CALL(mockSymbolQuery, sourceUsagesAt(_, 2, 5)); EXPECT_CALL(mockCallback, Call(usages));
engine.findUsages(CppTools::CursorInEditor{cursor, filePath}, engine.findUsages(CppTools::CursorInEditor{cursor, filePath}, mockCallback.AsStdFunction());
[](const CppTools::Usages &) {});
} }
TEST_F(RefactoringEngine, ExpectSourceUsagesAtInGlobalRename) TEST_F(RefactoringEngine, CallFindUsages)
{ {
cursor.setPosition(11); EXPECT_CALL(mockSymbolQuery, sourceUsagesAt(Eq(12), 2, 5));
EXPECT_CALL(mockSymbolQuery, sourceUsagesAt(_, 2, 5)); engine.findUsages(CppTools::CursorInEditor{cursor, filePath}, [](const CppTools::Usages &) {});
}
TEST_F(RefactoringEngine, FindUsagesWithInvalidCursor)
{
EXPECT_CALL(mockSymbolQuery, sourceUsagesAt(_, _, _)).Times(0);
engine.findUsages(CppTools::CursorInEditor{{}, filePath}, [](const CppTools::Usages &) {});
}
TEST_F(RefactoringEngine, CallSourceUsagesInInGlobalRename)
{
EXPECT_CALL(mockSymbolQuery, sourceUsagesAt(Eq(12), 2, 5));
engine.globalRename(CppTools::CursorInEditor{cursor, filePath}, engine.globalRename(CppTools::CursorInEditor{cursor, filePath},
[](const CppTools::Usages &) {}, QString()); [](const CppTools::Usages &) {},
{});
}
TEST_F(RefactoringEngine, CallSourceUsagesInInGlobalRenameWithInvalidCursor)
{
EXPECT_CALL(mockSymbolQuery, sourceUsagesAt(_, _, _)).Times(0);
engine.globalRename(CppTools::CursorInEditor{{}, filePath}, [](const CppTools::Usages &) {}, {});
}
TEST_F(RefactoringEngine, CallDeclarationsAtInInGlobalFollowSymbol)
{
EXPECT_CALL(mockSymbolQuery, declarationsAt(Eq(12), 2, 5));
engine.globalFollowSymbol(
CppTools::CursorInEditor{cursor, filePath}, [](const Utils::Link &) {}, {}, {}, {}, {});
}
TEST_F(RefactoringEngine, CallDeclarationsAtInInGlobalFollowSymbolWithInvalidCursor)
{
EXPECT_CALL(mockSymbolQuery, declarationsAt(_, _, _)).Times(0);
engine.globalFollowSymbol(
CppTools::CursorInEditor{{}, filePath}, [](const Utils::Link &) {}, {}, {}, {}, {});
}
TEST_F(RefactoringEngine, InGlobalFollowSymbol)
{
using Utils::Link;
NiceMock<MockFunction<void(const Link &)>> mockCallback;
ON_CALL(mockSymbolQuery, declarationsAt(Eq(12), 2, 5)).WillByDefault(Return(usages));
EXPECT_CALL(mockCallback,
Call(AllOf(Field(&Link::targetFileName, Eq("/path1")),
Field(&Link::targetLine, Eq(1)),
Field(&Link::targetColumn, Eq(2)))));
engine.globalFollowSymbol(
CppTools::CursorInEditor{cursor, filePath}, mockCallback.AsStdFunction(), {}, {}, {}, {});
}
TEST_F(RefactoringEngine, InGlobalFollowSymbolSkipCurrentFile)
{
using Utils::Link;
NiceMock<MockFunction<void(const Link &)>> mockCallback;
CppTools::Usages usages{{clangBackEndFilePath, 1, 3}, {"/path2", 4, 5}};
ON_CALL(mockSymbolQuery, declarationsAt(Eq(12), 2, 5)).WillByDefault(Return(usages));
EXPECT_CALL(mockCallback,
Call(AllOf(Field(&Link::targetFileName, Eq("/path2")),
Field(&Link::targetLine, Eq(4)),
Field(&Link::targetColumn, Eq(4)))));
engine.globalFollowSymbol(
CppTools::CursorInEditor{cursor, filePath}, mockCallback.AsStdFunction(), {}, {}, {}, {});
} }
TEST_F(RefactoringEngine, EngineIsNotUsableForUnusableServer) TEST_F(RefactoringEngine, EngineIsNotUsableForUnusableServer)

View File

@@ -61,12 +61,11 @@ protected:
MockSqliteReadStatement &selectSymbolsForKindAndStartsWith2 = mockStatementFactory.selectSymbolsForKindAndStartsWith2; MockSqliteReadStatement &selectSymbolsForKindAndStartsWith2 = mockStatementFactory.selectSymbolsForKindAndStartsWith2;
MockSqliteReadStatement &selectSymbolsForKindAndStartsWith3 = mockStatementFactory.selectSymbolsForKindAndStartsWith3; MockSqliteReadStatement &selectSymbolsForKindAndStartsWith3 = mockStatementFactory.selectSymbolsForKindAndStartsWith3;
MockSqliteReadStatement &selectLocationOfSymbol = mockStatementFactory.selectLocationOfSymbol; MockSqliteReadStatement &selectLocationOfSymbol = mockStatementFactory.selectLocationOfSymbol;
SourceLocations locations{{1, 1, 1}, MockSqliteReadStatement &selectSourceUsagesOrderedForSymbolLocation = mockStatementFactory
{1, 2, 3}, .selectSourceUsagesOrderedForSymbolLocation;
{2, 1, 1}, MockSqliteReadStatement &selectSourceUsagesByLocationKindForSymbolLocation
{2, 3, 1}, = mockStatementFactory.selectSourceUsagesByLocationKindForSymbolLocation;
{4, 1, 1}, SourceLocations locations{{1, 1, 1}, {1, 2, 3}, {2, 1, 1}, {2, 3, 1}, {4, 1, 1}, {4, 1, 3}};
{4, 1, 3}};
MockQuery query{mockStatementFactory}; MockQuery query{mockStatementFactory};
}; };
@@ -78,8 +77,9 @@ protected:
database.execute("INSERT INTO sources VALUES (1, 1, \"filename.h\")"); database.execute("INSERT INTO sources VALUES (1, 1, \"filename.h\")");
database.execute("INSERT INTO sources VALUES (2, 1, \"filename.cpp\")"); database.execute("INSERT INTO sources VALUES (2, 1, \"filename.cpp\")");
database.execute("INSERT INTO directories VALUES (1, \"/path/to\")"); database.execute("INSERT INTO directories VALUES (1, \"/path/to\")");
database.execute("INSERT INTO locations VALUES (1, 2, 3, 1, 1)"); database.execute("INSERT INTO locations VALUES (1, 2, 3, 1, 2)");
database.execute("INSERT INTO locations VALUES (1, 4, 6, 2, 3)"); database.execute("INSERT INTO locations VALUES (1, 4, 6, 2, 1)");
database.execute("INSERT INTO locations VALUES (1, 20, 36, 2, 3)");
database.execute("INSERT INTO symbols VALUES (1, \"functionusr\", \"Function\", 3, \"void function(int)\")"); database.execute("INSERT INTO symbols VALUES (1, \"functionusr\", \"Function\", 3, \"void function(int)\")");
database.execute("INSERT INTO symbols VALUES (2, \"classusr\", \"Class\", 2, \"class Class final\")"); database.execute("INSERT INTO symbols VALUES (2, \"classusr\", \"Class\", 2, \"class Class final\")");
database.execute("INSERT INTO symbols VALUES (3, \"enumusr\", \"Enum\", 1, \"enum Enum : char\")"); database.execute("INSERT INTO symbols VALUES (3, \"enumusr\", \"Enum\", 1, \"enum Enum : char\")");
@@ -105,7 +105,8 @@ TEST_F(SymbolQuerySlowTest, LocationsAt)
ASSERT_THAT(locations, ASSERT_THAT(locations,
UnorderedElementsAre(SourceLocation(1, 2, 3), UnorderedElementsAre(SourceLocation(1, 2, 3),
SourceLocation(2, 4, 6))); SourceLocation(2, 4, 6),
SourceLocation(2, 20, 36)));
} }
TEST_F(SymbolQuery, SourceUsagesAtCallsValues) TEST_F(SymbolQuery, SourceUsagesAtCallsValues)
@@ -121,7 +122,8 @@ TEST_F(SymbolQuerySlowTest, SourceUsagesAt)
ASSERT_THAT(usages, ASSERT_THAT(usages,
UnorderedElementsAre(CppTools::Usage("/path/to/filename.h", 2, 3), UnorderedElementsAre(CppTools::Usage("/path/to/filename.h", 2, 3),
CppTools::Usage("/path/to/filename.cpp", 4, 6))); CppTools::Usage("/path/to/filename.cpp", 4, 6),
CppTools::Usage("/path/to/filename.cpp", 20, 36)));
} }
TEST_F(SymbolQuery, SymbolsCallsValuesWithOneKindParameter) TEST_F(SymbolQuery, SymbolsCallsValuesWithOneKindParameter)
@@ -193,4 +195,39 @@ TEST_F(SymbolQuerySlowTest, LocationForSymbolId)
ASSERT_THAT(location.value(), Eq(SourceLocation(2, {4, 6}))); ASSERT_THAT(location.value(), Eq(SourceLocation(2, {4, 6})));
} }
TEST_F(SymbolQuery, SourceUsagesAtByLocationKindCallsValues)
{
EXPECT_CALL(selectSourceUsagesByLocationKindForSymbolLocation,
valuesReturnSourceUsages(
_, 42, 14, 7, static_cast<int>(ClangBackEnd::SourceLocationKind::Definition)));
query.sourceUsagesAtByLocationKind(42, 14, 7, ClangBackEnd::SourceLocationKind::Definition);
} }
TEST_F(SymbolQuerySlowTest, SourceUsagesAtByLocationKind)
{
auto usages = query.sourceUsagesAtByLocationKind(2,
4,
6,
ClangBackEnd::SourceLocationKind::Definition);
ASSERT_THAT(usages, ElementsAre(CppTools::Usage("/path/to/filename.cpp", 4, 6)));
}
TEST_F(SymbolQuery, DeclarationsAtCallsValues)
{
EXPECT_CALL(selectSourceUsagesOrderedForSymbolLocation, valuesReturnSourceUsages(_, 42, 14, 7));
query.declarationsAt(42, 14, 7);
}
TEST_F(SymbolQuerySlowTest, DeclarationsAt)
{
auto usages = query.declarationsAt(2, 4, 6);
ASSERT_THAT(usages,
ElementsAre(CppTools::Usage("/path/to/filename.cpp", 4, 6),
CppTools::Usage("/path/to/filename.h", 2, 3)));
}
} // namespace

View File

@@ -648,4 +648,44 @@ TEST_F(SymbolsCollector, ClearInputFilesAfterCollectingSymbols)
ASSERT_TRUE(collector.isClean()); ASSERT_TRUE(collector.isClean());
} }
TEST_F(SymbolsCollector, ClassDeclarations)
{
collector.setFile(filePathId(TESTDATA_DIR "/symbolscollector/class.cpp"), {"cc"});
collector.collectSymbols();
ASSERT_THAT(
collector.sourceLocations(),
AllOf(Contains(IsSourceLocationEntry(symbolId("Class"),
filePathId(TESTDATA_DIR "/symbolscollector/class.cpp"),
1,
7,
SourceLocationKind::Definition)),
Contains(IsSourceLocationEntry(symbolId("bar"),
filePathId(TESTDATA_DIR "/symbolscollector/class.cpp"),
8,
8,
SourceLocationKind::Definition)),
Contains(IsSourceLocationEntry(symbolId("foo"),
filePathId(TESTDATA_DIR "/symbolscollector/class.cpp"),
11,
13,
SourceLocationKind::Definition)),
Contains(IsSourceLocationEntry(symbolId("foo"),
filePathId(TESTDATA_DIR "/symbolscollector/class.cpp"),
6,
8,
SourceLocationKind::Declaration)),
Contains(IsSourceLocationEntry(symbolId("Class"),
filePathId(TESTDATA_DIR "/symbolscollector/class.cpp"),
11,
6,
SourceLocationKind::DeclarationReference)),
Contains(IsSourceLocationEntry(symbolId("bar"),
filePathId(TESTDATA_DIR "/symbolscollector/class.cpp"),
13,
5,
SourceLocationKind::DeclarationReference))));
}
} // namespace } // namespace