Implemented declaration finding feature
This commit is contained in:
parent
f2d121b71f
commit
75a6436f58
|
@ -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
44
main.d
|
@ -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`,
|
||||||
|
|
1
makefile
1
makefile
|
@ -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\
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue