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
* 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
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

62
main.d
View File

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

View File

@ -10,6 +10,7 @@ SRC = main.d\
ctags.d\
astprinter.d\
outliner.d\
symbol_finder.d\
libdparse/src/std/*.d\
libdparse/src/std/d/*.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;
}