1 //          Copyright Brian Schott (Hackerpilot) 2014.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module dscanner.symbol_finder;
7 
8 import std.stdio : File;
9 import dparse.lexer;
10 import dparse.parser;
11 import dparse.ast;
12 import dparse.rollback_allocator;
13 import std.stdio;
14 import std.file : isFile;
15 import std.functional : toDelegate;
16 
17 void findDeclarationOf(File output, string symbolName, string[] fileNames)
18 {
19 	findDeclarationOf((string fileName, size_t line, size_t column)
20 	{
21 		output.writefln("%s(%d:%d)", fileName, line, column);
22 	}, symbolName, fileNames);
23 }
24 
25 /// Delegate that gets called every time a declaration gets found
26 alias OutputHandler = void delegate(string fileName, size_t line, size_t column);
27 
28 /// Finds all declarations of a symbol in the given fileNames and calls a handler on every occurence.
29 /// Params:
30 ///   output = Callback which gets called when a declaration is found
31 ///   symbolName = Symbol name to search for
32 ///   fileNames = An array of file names which might contain stdin to read from stdin
33 void findDeclarationOf(scope OutputHandler output, string symbolName, string[] fileNames)
34 {
35 	import std.array : uninitializedArray, array;
36 	import std.conv : to;
37 
38 	LexerConfig config;
39 	StringCache cache = StringCache(StringCache.defaultBucketCount);
40 	auto visitor = new FinderVisitor(output, symbolName);
41 	foreach (fileName; fileNames)
42 	{
43 		File f = fileName == "stdin" ? std.stdio.stdin : File(fileName);
44 		assert(fileName == "stdin" || isFile(fileName));
45 		if (f.size == 0)
46 			continue;
47 		auto bytes = uninitializedArray!(ubyte[])(to!size_t(f.size));
48 		f.rawRead(bytes);
49 		auto tokens = getTokensForParser(bytes, config, &cache);
50 		RollbackAllocator rba;
51 		Module m = parseModule(tokens.array, fileName, &rba, toDelegate(&doNothing));
52 		visitor.fileName = fileName;
53 		visitor.visit(m);
54 	}
55 }
56 
57 private:
58 
59 void doNothing(string, size_t, size_t, string, bool)
60 {
61 }
62 
63 class FinderVisitor : ASTVisitor
64 {
65 	this(OutputHandler output, string symbolName)
66 	{
67 		this.output = output;
68 		this.symbolName = symbolName;
69 	}
70 
71 	mixin generateVisit!FunctionDeclaration;
72 	mixin generateVisit!ClassDeclaration;
73 	mixin generateVisit!InterfaceDeclaration;
74 	mixin generateVisit!StructDeclaration;
75 	mixin generateVisit!UnionDeclaration;
76 	mixin generateVisit!TemplateDeclaration;
77 
78 	override void visit(const EnumDeclaration dec)
79 	{
80 		if (dec.name.text == symbolName)
81 			output(fileName, dec.name.line, dec.name.column);
82 	}
83 
84 	override void visit(const AnonymousEnumMember member)
85 	{
86 		if (member.name.text == symbolName)
87 			output(fileName, member.name.line, member.name.column);
88 	}
89 
90 	override void visit(const EnumMember member)
91 	{
92 		if (member.name.text == symbolName)
93 			output(fileName, member.name.line, member.name.column);
94 	}
95 
96 	override void visit(const AliasDeclaration dec)
97 	{
98 		if (dec.declaratorIdentifierList !is null)
99 		{
100 			foreach (ident; dec.declaratorIdentifierList.identifiers)
101 			{
102 				if (ident.text == symbolName)
103 					output(fileName, ident.line, ident.column);
104 			}
105 		}
106 		foreach (initializer; dec.initializers)
107 		{
108 			if (initializer.name.text == symbolName)
109 				output(fileName, initializer.name.line,
110 						initializer.name.column);
111 		}
112 	}
113 
114 	override void visit(const Declarator dec)
115 	{
116 		if (dec.name.text == symbolName)
117 			output(fileName, dec.name.line, dec.name.column);
118 	}
119 
120 	override void visit(const AutoDeclaration ad)
121 	{
122 		foreach (part; ad.parts)
123 		{
124 			if (part.identifier.text == symbolName)
125 				output(fileName, part.identifier.line, part.identifier.column);
126 		}
127 	}
128 
129 	override void visit(const FunctionBody)
130 	{
131 	}
132 
133 	mixin template generateVisit(T)
134 	{
135 		override void visit(const T t)
136 		{
137 			if (t.name.text == symbolName)
138 				output(fileName, t.name.line, t.name.column);
139 			t.accept(this);
140 		}
141 	}
142 
143 	alias visit = ASTVisitor.visit;
144 
145 	OutputHandler output;
146 	string symbolName;
147 	string fileName;
148 }