Skip to content

Commit

Permalink
Fix issue #1152. Find dead functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
Andersbakken committed Jul 30, 2018
1 parent 731f7e9 commit a0aac2b
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 8 deletions.
42 changes: 40 additions & 2 deletions src/Project.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1804,17 +1804,23 @@ static Set<Symbol> findReferences(const Symbol &in,
return findReferences(inputs, project, std::move(filter));
}

Set<Symbol> Project::findCallers(const Symbol &symbol)
Set<Symbol> Project::findCallers(const Symbol &symbol, int max)
{
const bool isClazz = symbol.isClass();
return ::findReferences(symbol, shared_from_this(), [isClazz](const Symbol &input, const Symbol &ref) {
return ::findReferences(symbol, shared_from_this(), [isClazz, &max](const Symbol &input, const Symbol &ref) {
if (!max)
return false;
if (isClazz && (ref.isConstructorOrDestructor() || ref.kind == CXCursor_CallExpr))
return false;
if (ref.isReference()
|| (input.kind == CXCursor_Constructor && (ref.kind == CXCursor_VarDecl || ref.kind == CXCursor_FieldDecl))) {
if (max != -1)
--max;
return true;
}
if (input.kind == CXCursor_ClassTemplate && ref.flags & Symbol::TemplateSpecialization) {
if (max != -1)
--max;
return true;
}
return false;
Expand Down Expand Up @@ -2918,3 +2924,35 @@ bool Project::isTemplateDiagnostic(const std::pair<Location, Diagnostic> &diagno
}
return false;
}

Set<Symbol> Project::findDeadFunctions(uint32_t fileId)
{
Set<Symbol> ret;
auto processFile = [this, &ret](uint32_t file, Set<String> *seen = 0) {
auto symbols = openSymbols(file);
if (!symbols)
return;

const int count = symbols->count();
for (int i=0; i<count; ++i) {
Symbol s = symbols->valueAt(i);
if (RTags::isFunction(s.kind)
&& s.kind != CXCursor_Destructor
&& s.kind != CXCursor_LambdaExpr
&& !s.symbolName.startsWith("int main(")
&& (!seen || seen->insert(s.usr))
&& findCallers(s, 1).isEmpty()) {
ret.insert(std::move(s));
}
}
};
if (!fileId) {
Set<String> seenUsrs;
for (const auto &file : mVisitedFiles) {
processFile(file.first, &seenUsrs);
}
} else {
processFile(fileId);
}
return ret;
}
5 changes: 3 additions & 2 deletions src/Project.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ class Project : public std::enable_shared_from_this<Project>
All
};

Set<Symbol> findDeadFunctions(uint32_t fileId);
Set<uint32_t> dependencies(uint32_t fileId, DependencyMode mode) const;
bool dependsOn(uint32_t source, uint32_t header) const;
String dumpDependencies(uint32_t fileId,
Expand Down Expand Up @@ -163,8 +164,8 @@ class Project : public std::enable_shared_from_this<Project>
Symbol findTarget(const Symbol &symbol) { return RTags::bestTarget(findTargets(symbol)); }
Set<Symbol> findAllReferences(Location location) { return findAllReferences(findSymbol(location)); }
Set<Symbol> findAllReferences(const Symbol &symbol);
Set<Symbol> findCallers(Location location) { return findCallers(findSymbol(location)); }
Set<Symbol> findCallers(const Symbol &symbol);
Set<Symbol> findCallers(Location location, int max = -1) { return findCallers(findSymbol(location), max); }
Set<Symbol> findCallers(const Symbol &symbol, int max = -1);
Set<Symbol> findVirtuals(Location location) { return findVirtuals(findSymbol(location)); }
Set<Symbol> findVirtuals(const Symbol &symbol);
Set<String> findTargetUsrs(const Symbol &symbol);
Expand Down
1 change: 1 addition & 0 deletions src/QueryMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class QueryMessage : public RTagsMessage
ClassHierarchy,
ClearProjects,
CodeCompleteAt,
DeadFunctions,
DebugLocations,
DeleteProject,
Dependencies,
Expand Down
13 changes: 10 additions & 3 deletions src/RClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ std::initializer_list<CommandLineParser::Option<RClient::OptionType> > opts = {
#endif
{ RClient::Validate, "validate", 0, CommandLineParser::NoValue, "Validate database files for current project." },
{ RClient::Tokens, "tokens", 0, CommandLineParser::Required, "Dump tokens for file. --tokens file.cpp:123-321 for range." },
{ RClient::DeadFunctions, "find-dead-functions", 0, CommandLineParser::Optional, "Find functions declared/defined in the current file that are never in the project." },

{ RClient::None, String(), 0, CommandLineParser::NoValue, "" },
{ RClient::None, String(), 0, CommandLineParser::NoValue, "Command flags:" },
{ RClient::StripParen, "strip-paren", 'p', CommandLineParser::NoValue, "Strip parens in various contexts." },
Expand Down Expand Up @@ -1154,13 +1156,14 @@ CommandLineParser::ParseStatus RClient::parse(size_t argc, char **argv)
case CheckIncludes:
case GenerateTest:
case Diagnose:
case DeadFunctions:
case FixIts: {
Path p = std::move(value);
if (!p.exists()) {
if (!p.exists() && (!p.isEmpty() || type != DeadFunctions)) {
return { String::format<1024>("%s does not exist", p.constData()), CommandLineParser::Parse_Error };
}

if (!p.isAbsolute())
if (!p.isAbsolute() && !p.isEmpty())
p.prepend(Path::pwd());

if (p.isDir()) {
Expand All @@ -1170,7 +1173,8 @@ CommandLineParser::ParseStatus RClient::parse(size_t argc, char **argv)
p.append('/');
}
}
p.resolve();
if (!p.isEmpty())
p.resolve();
Flags<QueryMessage::Flag> extraQueryFlags;
QueryMessage::Type queryType = QueryMessage::Invalid;
switch (type) {
Expand All @@ -1183,6 +1187,9 @@ CommandLineParser::ParseStatus RClient::parse(size_t argc, char **argv)
case DumpFile:
queryType = QueryMessage::DumpFile;
break;
case DeadFunctions:
queryType = QueryMessage::DeadFunctions;
break;
case CheckIncludes:
queryType = QueryMessage::DumpFile;
extraQueryFlags |= QueryMessage::DumpCheckIncludes;
Expand Down
1 change: 1 addition & 0 deletions src/RClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class RClient
CurrentFile,
CurrentProject,
CursorKind,
DeadFunctions,
DebugLocations,
DeclarationOnly,
DefinitionOnly,
Expand Down
64 changes: 64 additions & 0 deletions src/Server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,9 @@ void Server::handleQueryMessage(const std::shared_ptr<QueryMessage> &message, co
case QueryMessage::SendDiagnostics:
sendDiagnostics(message, conn);
break;
case QueryMessage::DeadFunctions:
deadFunctions(message, conn);
break;
case QueryMessage::CodeCompleteAt:
codeCompleteAt(message, conn);
break;
Expand Down Expand Up @@ -1651,6 +1654,67 @@ void Server::jobCount(const std::shared_ptr<QueryMessage> &query, const std::sha
conn->finish();
}

void Server::deadFunctions(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Connection> &conn)
{
std::shared_ptr<Project> project = projectForQuery(query);
if (!project)
project = currentProject();
if (project) {
class DeadFunctionsJob : public QueryJob
{
public:
DeadFunctionsJob(const std::shared_ptr<QueryMessage> &msg, const std::shared_ptr<Project> &project)
: QueryJob(msg, project)
{}
virtual int execute() override
{
const uint32_t fileId = Location::fileId(queryMessage()->query());
if (fileId)
Server::instance()->prepareCompletion(queryMessage(), fileId, project());
bool raw = false;
if (!(queryFlags() & (QueryMessage::JSON|QueryMessage::Elisp))) {
raw = true;
setPieceFilters(std::move(Set<String>() << "location"));
}
bool failed = false;
const std::shared_ptr<Project> proj = project();
auto process = [this, proj, &failed](uint32_t file) {
for (const Symbol &symbol : proj->findDeadFunctions(file)) {
if (!failed && !write(symbol))
failed = true;
}
};
if (!fileId) {
Set<uint32_t> all = proj->dependencies(0, Project::All);
all.remove([](uint32_t file) { return Location::path(file).isSystem(); });
size_t idx = 0;
const Path projectPath = proj->path();
for (uint32_t file : proj->dependencies(0, Project::All)) {
if (raw) {
Path p = Location::path(file);
const char *ch = p.constData();
if (!(queryFlags() & QueryMessage::AbsolutePath) && p.startsWith(projectPath))
ch += projectPath.size();
if (!write(String::format<256>("%zu/%zu %s", ++idx, all.size(), ch))) {
failed = true;
break;
}
}
process(file);
if (failed)
break;
}
} else {
process(fileId);
}
return 0;
}
} job(query, project);
job.run(conn);
}
conn->finish();
}

void Server::sendDiagnostics(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Connection> &conn)
{
if (testLog(RTags::DiagnosticsLevel))
Expand Down
1 change: 1 addition & 0 deletions src/Server.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ class Server
// Queries
void sendDiagnostics(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Connection> &conn);
void clearProjects(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Connection> &conn);
void deadFunctions(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Connection> &conn);
void codeCompleteAt(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Connection> &conn);
void symbolInfo(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Connection> &conn);
void dependencies(const std::shared_ptr<QueryMessage> &query, const std::shared_ptr<Connection> &conn);
Expand Down
17 changes: 16 additions & 1 deletion src/rtags.el
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconst rtags-protocol-version 126)
(defconst rtags-protocol-version 127)
(defconst rtags-package-version "2.18")
(defconst rtags-popup-available (require 'popup nil t))
(defconst rtags-supported-major-modes '(c-mode c++-mode objc-mode) "Major modes RTags supports.")
Expand Down Expand Up @@ -1655,6 +1655,21 @@ instead of file from `current-buffer'.
(rtags-call-rc :path fn "--dependencies" fn args (unless rtags-print-filenames-relative "-K"))
(rtags-mode))))

(defun rtags-find-dead-functions (&optional prefix buffer)
"Print information about uncalled functions in buffer."
(interactive "P")
(let ((dead-functions-buffer (rtags-get-buffer)))
(rtags-delete-rtags-windows)
(rtags-location-stack-push)
(rtags-switch-to-buffer dead-functions-buffer)
(if prefix
(rtags-call-rc "--find-dead-functions" (unless rtags-print-filenames-relative "-K"))
(let ((fn (rtags-buffer-file-name buffer)))
(unless fn
(rtags--error 'rtags-no-file-here))
(rtags-call-rc :path fn "--find-dead-functions" fn (unless rtags-print-filenames-relative "-K"))))
(rtags-mode)))

;;;###autoload

(defvar rtags-dependency-tree-data nil)
Expand Down

0 comments on commit a0aac2b

Please sign in to comment.