D-Scanner/src/dscanner/imports.d

169 lines
4.2 KiB
D

// Copyright Brian Schott (Hackerpilot) 2012.
// 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 dscanner.imports;
import std.stdio;
import std.container.rbtree;
import std.functional : toDelegate;
import dscanner.utils;
import dmd.permissivevisitor;
import dmd.transitivevisitor;
import dmd.tokens;
import dmd.common.outbuffer;
import core.stdc.stdio;
import dmd.parse;
import dmd.astbase;
import dmd.id;
import dmd.globals;
import dmd.identifier;
import core.memory;
import std.stdio;
import std.file;
import std.conv : to;
extern(C++) class ImportVisitor(AST) : ParseTimeTransitiveVisitor!AST
{
alias visit = ParseTimeTransitiveVisitor!AST.visit;
this()
{
imports = new RedBlackTree!string;
}
override void visit(AST.Import imp)
{
import std.conv : to;
string s;
foreach (const pid; imp.packages)
s = s ~ to!string(pid.toChars()) ~ ".";
s ~= to!string(imp.id.toChars());
imports.insert(s);
}
RedBlackTree!string imports;
}
private void visitFile(bool usingStdin, string fileName, RedBlackTree!string importedModules)
{
import dmd.errorsink : ErrorSinkNull;
Id.initialize();
global._init();
global.params.useUnitTests = true;
ASTBase.Type._init();
auto id = Identifier.idPool(fileName);
auto m = new ASTBase.Module(&(fileName.dup)[0], id, false, false);
ubyte[] bytes = usingStdin ? readStdin() : readFile(fileName);
auto input = cast(char[]) bytes;
__gshared ErrorSinkNull errorSinkNull;
if (!errorSinkNull)
errorSinkNull = new ErrorSinkNull;
scope p = new Parser!ASTBase(m, input, false, new ErrorSinkNull, null, false);
p.nextToken();
m.members = p.parseModule();
scope vis = new ImportVisitor!ASTBase();
m.accept(vis);
importedModules.insert(vis.imports[]);
}
private void doNothing(string, size_t, size_t, string, bool)
{
}
void printImports(bool usingStdin, string[] args, string[] importPaths, 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);
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);
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);
}
unittest
{
import std.stdio : File;
import std.file : exists, remove;
auto deleteme = "test.txt";
File file = File(deleteme, "w");
scope(exit)
{
assert(exists(deleteme));
remove(deleteme);
}
file.write(q{
import std.stdio;
import std.fish : scales, head;
import DAGRON = std.experimental.dragon;
import std.file;
});
file.close();
auto importedFiles = new RedBlackTree!(string);
visitFile(false, deleteme, importedFiles);
auto expected = new RedBlackTree!(string);
expected.insert("std.stdio");
expected.insert("std.fish");
expected.insert("std.file");
expected.insert("std.experimental.dragon");
assert(expected == importedFiles);
}