Implemented declaration finding feature

This commit is contained in:
Hackerpilot 2014-08-13 17:08:28 -07:00
parent f2d121b71f
commit 75a6436f58
4 changed files with 130 additions and 29 deletions

View File

@ -68,6 +68,15 @@ the given source files.
* Public declarations not documented * Public declarations not documented
* Assignment in conditionals * Assignment in conditionals
### Find Declaration
Ack, grep, and The Silver Searcher are useful for finding usages of symbols, but
their signal to noise ratio is not very good when searching for a symbol's
declaration. The "--declaration" or "-d" options allow you to search for a
symbols declaration. For example:
$ dscanner -d TokenStructure
./libdparse/src/std/lexer.d(248:8)
### Line of Code Count ### Line of Code Count
The "--sloc" or "-l" option prints the number of lines of code in the file. The "--sloc" or "-l" option prints the number of lines of code in the file.
Instead of simply printing the number of line breaks, this counts the number of Instead of simply printing the number of line breaks, this counts the number of

44
main.d
View File

@ -23,6 +23,7 @@ import ctags;
import astprinter; import astprinter;
import imports; import imports;
import outliner; import outliner;
import symbol_finder;
import analysis.run; import analysis.run;
import analysis.config; import analysis.config;
@ -45,7 +46,6 @@ int run(string[] args)
bool sloc; bool sloc;
bool highlight; bool highlight;
bool ctags; bool ctags;
bool recursive;
bool help; bool help;
bool tokenCount; bool tokenCount;
bool syntaxCheck; bool syntaxCheck;
@ -56,15 +56,17 @@ int run(string[] args)
bool tokenDump; bool tokenDump;
bool styleCheck; bool styleCheck;
bool defaultConfig; bool defaultConfig;
string symbolName;
try try
{ {
getopt(args, std.getopt.config.caseSensitive, "sloc|l", &sloc, getopt(args, std.getopt.config.caseSensitive, "sloc|l", &sloc,
"highlight", &highlight, "ctags|c", &ctags, "recursive|r|R", &recursive, "highlight", &highlight, "ctags|c", &ctags, "help|h", &help,
"help|h", &help, "tokenCount|t", &tokenCount, "syntaxCheck|s", &syntaxCheck, "tokenCount|t", &tokenCount, "syntaxCheck|s", &syntaxCheck,
"ast|xml", &ast, "imports|i", &imports, "outline|o", &outline, "ast|xml", &ast, "imports|i", &imports, "outline|o", &outline,
"tokenDump", &tokenDump, "styleCheck|S", &styleCheck, "tokenDump", &tokenDump, "styleCheck|S", &styleCheck,
"defaultConfig", &defaultConfig, "muffinButton", &muffin); "defaultConfig", &defaultConfig, "declaration|d", &symbolName,
"muffinButton", &muffin);
} }
catch (ConvException e) catch (ConvException e)
{ {
@ -94,7 +96,8 @@ int run(string[] args)
} }
auto optionCount = count!"a"([sloc, highlight, ctags, tokenCount, auto optionCount = count!"a"([sloc, highlight, ctags, tokenCount,
syntaxCheck, ast, imports, outline, tokenDump, styleCheck, defaultConfig]); syntaxCheck, ast, imports, outline, tokenDump, styleCheck, defaultConfig,
symbolName !is null]);
if (optionCount > 1) if (optionCount > 1)
{ {
stderr.writeln("Too many options specified"); stderr.writeln("Too many options specified");
@ -138,9 +141,13 @@ int run(string[] args)
return 0; return 0;
} }
} }
else if (symbolName !is null)
{
stdout.findDeclarationOf(symbolName, expandArgs(args));
}
else if (ctags) else if (ctags)
{ {
stdout.printCtags(expandArgs(args, recursive)); stdout.printCtags(expandArgs(args));
} }
else if (styleCheck) else if (styleCheck)
{ {
@ -148,11 +155,11 @@ int run(string[] args)
string s = getConfigurationLocation(); string s = getConfigurationLocation();
if (s.exists()) if (s.exists())
readINIFile(config, s); readINIFile(config, s);
stdout.analyze(expandArgs(args, recursive), config); stdout.analyze(expandArgs(args), config);
} }
else if (syntaxCheck) else if (syntaxCheck)
{ {
stdout.syntaxCheck(expandArgs(args, recursive)); stdout.syntaxCheck(expandArgs(args));
} }
else else
{ {
@ -172,7 +179,7 @@ int run(string[] args)
else else
{ {
ulong count; ulong count;
foreach (f; expandArgs(args, recursive)) foreach (f; expandArgs(args))
{ {
LexerConfig config; LexerConfig config;
@ -237,14 +244,14 @@ int run(string[] args)
return 0; return 0;
} }
string[] expandArgs(string[] args, bool recursive) string[] expandArgs(string[] args)
{ {
if (recursive)
{
string[] rVal; string[] rVal;
if (args.length == 1)
args ~= ".";
foreach (arg; args[1 ..$]) foreach (arg; args[1 ..$])
{ {
if (isFile(arg) && arg.endsWith(`.d`) || arg.endsWith(`.di`)) if (isFile(arg) && (arg.endsWith(`.d`) || arg.endsWith(`.di`)))
rVal ~= arg; rVal ~= arg;
else foreach (item; dirEntries(arg, SpanMode.breadth).map!(a => a.name)) else foreach (item; dirEntries(arg, SpanMode.breadth).map!(a => a.name))
{ {
@ -255,9 +262,6 @@ string[] expandArgs(string[] args, bool recursive)
} }
} }
return rVal; return rVal;
}
else
return args[1 .. $];
} }
ubyte[] readStdin() ubyte[] readStdin()
@ -332,10 +336,10 @@ options:
Generates an XML representation of the source files abstract syntax Generates an XML representation of the source files abstract syntax
tree. If no files are specified, input is read from stdin. tree. If no files are specified, input is read from stdin.
--recursive | -R | -r --declaration | -d symbolName [sourceFiles sourceDirectories]
When used with --ctags, --tokenCount, or --sloc, dscanner will produce Find the location where symbolName is declared. This should be more
ctags output for all .d and .di files contained within the given accurate than "grep". Searches the given files and directories, or the
directories and its sub-directories. current working directory if none are specified.
--defaultConfig --defaultConfig
Generates a default configuration file for the static analysis checks`, Generates a default configuration file for the static analysis checks`,

View File

@ -10,6 +10,7 @@ SRC = main.d\
ctags.d\ ctags.d\
astprinter.d\ astprinter.d\
outliner.d\ outliner.d\
symbol_finder.d\
libdparse/src/std/*.d\ libdparse/src/std/*.d\
libdparse/src/std/d/*.d\ libdparse/src/std/d/*.d\
analysis/*.d\ analysis/*.d\

87
symbol_finder.d Normal file
View File

@ -0,0 +1,87 @@
// Copyright Brian Schott (Hackerpilot) 2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
module symbol_finder;
import std.stdio : File;
import std.d.lexer;
import std.d.parser;
import std.d.ast;
import std.stdio;
import std.file:isFile;
void findDeclarationOf(File output, string symbolName, string[] fileNames)
{
import std.array : uninitializedArray, array;
import std.conv : to;
LexerConfig config;
StringCache cache = StringCache(StringCache.defaultBucketCount);
auto visitor = new FinderVisitor(output, symbolName);
foreach (fileName; fileNames)
{
File f = File(fileName);
assert (isFile(fileName));
if (f.size == 0) continue;
auto bytes = uninitializedArray!(ubyte[])(to!size_t(f.size));
f.rawRead(bytes);
auto tokens = getTokensForParser(bytes, config, &cache);
Module m = parseModule(tokens.array, fileName, null, &doNothing);
visitor.fileName = fileName;
visitor.visit(m);
}
}
private:
void doNothing(string, size_t, size_t, string, bool) {}
class FinderVisitor : ASTVisitor
{
this(File output, string symbolName)
{
this.output = output;
this.symbolName = symbolName;
}
mixin generateVisit!FunctionDeclaration;
mixin generateVisit!ClassDeclaration;
mixin generateVisit!InterfaceDeclaration;
mixin generateVisit!StructDeclaration;
mixin generateVisit!UnionDeclaration;
mixin generateVisit!TemplateDeclaration;
override void visit(const Declarator dec)
{
if (dec.name.text == symbolName)
output.writefln("%s(%d:%d)", fileName, dec.name.line, dec.name.column);
}
override void visit (const AutoDeclaration ad)
{
foreach (id; ad.identifiers)
{
if (id.text == symbolName)
output.writefln("%s(%d:%d)", fileName, id.line, id.column);
}
}
override void visit(const FunctionBody) {}
mixin template generateVisit(T)
{
override void visit(const T t)
{
if (t.name.text == symbolName)
output.writefln("%s(%d:%d)", fileName, t.name.line, t.name.column);
t.accept(this);
}
}
alias visit = ASTVisitor.visit;
File output;
string symbolName;
string fileName;
}