Fix #355
This commit is contained in:
parent
d7ab1023a7
commit
5994c760a4
14
README.md
14
README.md
|
@ -34,6 +34,20 @@ source file.
|
|||
$ dscanner --imports helloworld.d
|
||||
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
|
||||
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
|
||||
|
|
|
@ -6,8 +6,12 @@
|
|||
module imports;
|
||||
|
||||
import dparse.ast;
|
||||
import dparse.lexer;
|
||||
import dparse.parser;
|
||||
import dparse.rollback_allocator;
|
||||
import std.stdio;
|
||||
import std.container;
|
||||
import std.container.rbtree;
|
||||
import readers;
|
||||
|
||||
/**
|
||||
* AST visitor that collects modules imported to an R-B tree.
|
||||
|
@ -50,3 +54,74 @@ class ImportPrinter : ASTVisitor
|
|||
private:
|
||||
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);
|
||||
}
|
||||
|
|
62
src/main.d
62
src/main.d
|
@ -53,13 +53,14 @@ else
|
|||
bool syntaxCheck;
|
||||
bool ast;
|
||||
bool imports;
|
||||
bool recursiveImports;
|
||||
bool muffin;
|
||||
bool outline;
|
||||
bool tokenDump;
|
||||
bool styleCheck;
|
||||
bool defaultConfig;
|
||||
bool report;
|
||||
bool skipTests;
|
||||
bool skipTests;
|
||||
string symbolName;
|
||||
string configLocation;
|
||||
string[] importPaths;
|
||||
|
@ -80,6 +81,7 @@ else
|
|||
"syntaxCheck|s", &syntaxCheck,
|
||||
"ast|xml", &ast,
|
||||
"imports|i", &imports,
|
||||
"recursiveImports", &recursiveImports,
|
||||
"outline|o", &outline,
|
||||
"tokenDump", &tokenDump,
|
||||
"styleCheck|S", &styleCheck,
|
||||
|
@ -91,7 +93,7 @@ else
|
|||
"version", &printVersion,
|
||||
"muffinButton", &muffin,
|
||||
"explore", &explore,
|
||||
"skipTests", &skipTests);
|
||||
"skipTests", &skipTests);
|
||||
//dfmt on
|
||||
}
|
||||
catch (ConvException e)
|
||||
|
@ -149,7 +151,7 @@ else
|
|||
|
||||
immutable optionCount = count!"a"([sloc, highlight, ctags, tokenCount, syntaxCheck, ast, imports,
|
||||
outline, tokenDump, styleCheck, defaultConfig, report,
|
||||
symbolName !is null, etags, etagsAll]);
|
||||
symbolName !is null, etags, etagsAll, recursiveImports]);
|
||||
if (optionCount > 1)
|
||||
{
|
||||
stderr.writeln("Too many options specified");
|
||||
|
@ -262,23 +264,9 @@ else
|
|||
writefln("total:\t%d", count);
|
||||
}
|
||||
}
|
||||
else if (imports)
|
||||
else if (imports || recursiveImports)
|
||||
{
|
||||
string[] fileNames = usingStdin ? ["stdin"] : expandArgs(args);
|
||||
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);
|
||||
printImports(usingStdin, args, importPaths, &cache, recursiveImports);
|
||||
}
|
||||
else if (ast || outline)
|
||||
{
|
||||
|
@ -307,36 +295,6 @@ else
|
|||
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)
|
||||
{
|
||||
stderr.writefln(`
|
||||
|
@ -364,7 +322,11 @@ Options:
|
|||
|
||||
--imports <file>, -i <file>
|
||||
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>
|
||||
Lexes and parses sourceFile, printing the line and column number of any
|
||||
|
|
|
@ -33,3 +33,37 @@ ubyte[] readFile(string fileName)
|
|||
f.rawRead(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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue