This commit is contained in:
Hackerpilot 2016-06-10 18:27:59 -07:00
parent d7ab1023a7
commit 5994c760a4
4 changed files with 136 additions and 51 deletions

View File

@ -34,6 +34,20 @@ source file.
$ dscanner --imports helloworld.d $ dscanner --imports helloworld.d
std.stdio std.stdio
Passing "-I" arguments (import locations) will cause D-Scanner to also attempt
to resolve the locations of the imported modules.
$ dscanner --imports helloworld.d -I ~/.dvm/compilers/dmd-2.071.1-b2/src/phobos/ -I ~/.dvm/compilers/dmd-2.071.1-b2/src/druntime/src/
/home/brian/.dvm/compilers/dmd-2.071.1-b2/src/phobos/std/stdio.d
The "--recursiveImports" option is similar to "--imports", except that it lists
imports of imports (and so on) recursively. The recursive import option requires
import paths to be specified in order to work correctly.
Limitations:
* The import listing feature DOES NOT IGNORE imports that may be unused to to `version` or `static if`.
* The import listing DOES NOT INCLUDE imports introduced by mixins.
### Syntax Check ### Syntax Check
The "--syntaxCheck" or "-s" option prints a listing of any errors or warnings found The "--syntaxCheck" or "-s" option prints a listing of any errors or warnings found
while lexing or parsing the given source file. It does not do any semantic while lexing or parsing the given source file. It does not do any semantic

View File

@ -6,8 +6,12 @@
module imports; module imports;
import dparse.ast; import dparse.ast;
import dparse.lexer;
import dparse.parser;
import dparse.rollback_allocator;
import std.stdio; import std.stdio;
import std.container; import std.container.rbtree;
import readers;
/** /**
* AST visitor that collects modules imported to an R-B tree. * AST visitor that collects modules imported to an R-B tree.
@ -50,3 +54,74 @@ class ImportPrinter : ASTVisitor
private: private:
bool ignore = true; bool ignore = true;
} }
private void visitFile(bool usingStdin, string fileName, RedBlackTree!string importedModules, StringCache* cache)
{
RollbackAllocator rba;
LexerConfig config;
config.fileName = fileName;
config.stringBehavior = StringBehavior.source;
auto visitor = new ImportPrinter;
auto tokens = getTokensForParser(usingStdin ? readStdin() : readFile(fileName), config, cache);
auto mod = parseModule(tokens, fileName, &rba, &doNothing);
visitor.visit(mod);
importedModules.insert(visitor.imports[]);
}
private void doNothing(string, size_t, size_t, string, bool)
{
}
void printImports(bool usingStdin, string[] args, string[] importPaths, StringCache* cache, bool recursive)
{
string[] fileNames = usingStdin ? ["stdin"] : expandArgs(args);
import std.path : buildPath, dirSeparator;
import std.file : isFile, exists;
import std.array : replace, empty;
import std.range : chain, only;
auto resolvedModules = new RedBlackTree!(string);
auto resolvedLocations = new RedBlackTree!(string);
auto importedFiles = new RedBlackTree!(string);
foreach (name; fileNames)
visitFile(usingStdin, name, importedFiles, cache);
if (importPaths.empty)
{
foreach (item; importedFiles[])
writeln(item);
return;
}
while (!importedFiles.empty)
{
auto newlyDiscovered = new RedBlackTree!(string);
itemLoop: foreach (item; importedFiles[])
{
foreach (path; importPaths)
{
auto d = buildPath(path, item.replace(".", dirSeparator) ~ ".d");
auto di = buildPath(path, item.replace(".", dirSeparator) ~ ".di");
auto p = buildPath(path, item.replace(".", dirSeparator), "package.d");
auto pi = buildPath(path, item.replace(".", dirSeparator), "package.di");
foreach (alt; [d, di, p, pi])
{
if (exists(alt) && isFile(alt))
{
resolvedModules.insert(item);
resolvedLocations.insert(alt);
if (recursive)
visitFile(false, alt, newlyDiscovered, cache);
continue itemLoop;
}
}
}
writeln("Could not resolve location of ", item);
}
foreach (item; importedFiles[])
newlyDiscovered.removeKey(item);
foreach (resolved; resolvedModules[])
newlyDiscovered.removeKey(resolved);
importedFiles = newlyDiscovered;
}
foreach (resolved; resolvedLocations[])
writeln(resolved);
}

View File

@ -53,13 +53,14 @@ else
bool syntaxCheck; bool syntaxCheck;
bool ast; bool ast;
bool imports; bool imports;
bool recursiveImports;
bool muffin; bool muffin;
bool outline; bool outline;
bool tokenDump; bool tokenDump;
bool styleCheck; bool styleCheck;
bool defaultConfig; bool defaultConfig;
bool report; bool report;
bool skipTests; bool skipTests;
string symbolName; string symbolName;
string configLocation; string configLocation;
string[] importPaths; string[] importPaths;
@ -80,6 +81,7 @@ else
"syntaxCheck|s", &syntaxCheck, "syntaxCheck|s", &syntaxCheck,
"ast|xml", &ast, "ast|xml", &ast,
"imports|i", &imports, "imports|i", &imports,
"recursiveImports", &recursiveImports,
"outline|o", &outline, "outline|o", &outline,
"tokenDump", &tokenDump, "tokenDump", &tokenDump,
"styleCheck|S", &styleCheck, "styleCheck|S", &styleCheck,
@ -91,7 +93,7 @@ else
"version", &printVersion, "version", &printVersion,
"muffinButton", &muffin, "muffinButton", &muffin,
"explore", &explore, "explore", &explore,
"skipTests", &skipTests); "skipTests", &skipTests);
//dfmt on //dfmt on
} }
catch (ConvException e) catch (ConvException e)
@ -149,7 +151,7 @@ else
immutable optionCount = count!"a"([sloc, highlight, ctags, tokenCount, syntaxCheck, ast, imports, immutable optionCount = count!"a"([sloc, highlight, ctags, tokenCount, syntaxCheck, ast, imports,
outline, tokenDump, styleCheck, defaultConfig, report, outline, tokenDump, styleCheck, defaultConfig, report,
symbolName !is null, etags, etagsAll]); symbolName !is null, etags, etagsAll, recursiveImports]);
if (optionCount > 1) if (optionCount > 1)
{ {
stderr.writeln("Too many options specified"); stderr.writeln("Too many options specified");
@ -262,23 +264,9 @@ else
writefln("total:\t%d", count); writefln("total:\t%d", count);
} }
} }
else if (imports) else if (imports || recursiveImports)
{ {
string[] fileNames = usingStdin ? ["stdin"] : expandArgs(args); printImports(usingStdin, args, importPaths, &cache, recursiveImports);
RollbackAllocator rba;
LexerConfig config;
config.stringBehavior = StringBehavior.source;
auto visitor = new ImportPrinter;
foreach (name; fileNames)
{
config.fileName = name;
auto tokens = getTokensForParser(usingStdin ? readStdin()
: readFile(name), config, &cache);
auto mod = parseModule(tokens, name, &rba, &doNothing);
visitor.visit(mod);
}
foreach (imp; visitor.imports[])
writeln(imp);
} }
else if (ast || outline) else if (ast || outline)
{ {
@ -307,36 +295,6 @@ else
return 0; return 0;
} }
string[] expandArgs(string[] args)
{
// isFile can throw if it's a broken symlink.
bool isFileSafe(T)(T a)
{
try
return isFile(a);
catch (FileException)
return false;
}
string[] rVal;
if (args.length == 1)
args ~= ".";
foreach (arg; args[1 .. $])
{
if (isFileSafe(arg))
rVal ~= arg;
else
foreach (item; dirEntries(arg, SpanMode.breadth).map!(a => a.name))
{
if (isFileSafe(item) && (item.endsWith(`.d`) || item.endsWith(`.di`)))
rVal ~= item;
else
continue;
}
}
return rVal;
}
void printHelp(string programName) void printHelp(string programName)
{ {
stderr.writefln(` stderr.writefln(`
@ -364,7 +322,11 @@ Options:
--imports <file>, -i <file> --imports <file>, -i <file>
Prints modules imported by the given source file. If no files are Prints modules imported by the given source file. If no files are
specified, input is read from stdin. specified, input is read from stdin. Combine with "-I" arguments to
resolve import locations.
--recursive-imports <file>
Similar to "--imports", but lists imports of imports recursively.
--syntaxCheck <file>, -s <file> --syntaxCheck <file>, -s <file>
Lexes and parses sourceFile, printing the line and column number of any Lexes and parses sourceFile, printing the line and column number of any

View File

@ -33,3 +33,37 @@ ubyte[] readFile(string fileName)
f.rawRead(sourceCode); f.rawRead(sourceCode);
return sourceCode; return sourceCode;
} }
string[] expandArgs(string[] args)
{
import std.file : isFile, FileException, dirEntries, SpanMode;
import std.algorithm.iteration : map;
import std.algorithm.searching : endsWith;
// isFile can throw if it's a broken symlink.
bool isFileSafe(T)(T a)
{
try
return isFile(a);
catch (FileException)
return false;
}
string[] rVal;
if (args.length == 1)
args ~= ".";
foreach (arg; args[1 .. $])
{
if (isFileSafe(arg))
rVal ~= arg;
else
foreach (item; dirEntries(arg, SpanMode.breadth).map!(a => a.name))
{
if (isFileSafe(item) && (item.endsWith(`.d`) || item.endsWith(`.di`)))
rVal ~= item;
else
continue;
}
}
return rVal;
}