1 module dmdtags.generate;
2 
3 import dmdtags.tag: Kind;
4 import dmdtags.appender: Appender;
5 import dmdtags.span;
6 
7 import dmd.declaration: AliasDeclaration, VarDeclaration;
8 import dmd.func: FuncDeclaration;
9 import dmd.denum: EnumDeclaration, EnumMember;
10 import dmd.dversion: VersionSymbol;
11 import dmd.dstruct: StructDeclaration, UnionDeclaration;
12 import dmd.dclass: ClassDeclaration, InterfaceDeclaration;
13 import dmd.dtemplate: TemplateDeclaration;
14 import dmd.nspace: Nspace;
15 import dmd.dmodule: Module;
16 import dmd.dsymbol: Dsymbol;
17 import dmd.visitor: Visitor, SemanticTimeTransitiveVisitor;
18 
19 import std.meta: AliasSeq;
20 
21 void putTag(ref Appender!(Span!(const(char))) sink, Dsymbol sym, bool isPrivate)
22 {
23 	import dmd.root..string: toDString;
24 	import std.range: put;
25 	import std.format: format;
26 
27 	if (!sym.loc.isValid) return;
28 	if (!sym.ident) return;
29 
30 	const(char)[] filename = sym.loc.filename.toDString;
31 	if (!filename) return;
32 
33 	const(char)[] tag = format(
34 		"%s\t%s\t%s;\"\t%s",
35 		sym.ident.toString, filename, sym.loc.linnum, cast(char) sym.tagKind
36 	);
37 
38 	if (isPrivate) {
39 		tag ~= "\tfile:";
40 	}
41 
42 	put(sink, tag.span.headMutable);
43 }
44 
45 void putTag(ref Appender!(Span!(const(char))) sink, Module m, bool isPrivate)
46 {
47 	import std.range: put;
48 	import std.format: format;
49 
50 	if (!m.srcfile.name) return;
51 
52 	size_t line = m.md ? m.md.loc.linnum : 1;
53 	const(char)[] tag = format(
54 		"%s\t%s\t%s;\"\t%s",
55 		m.ident.toString, m.srcfile.toString, line, cast(char) m.tagKind
56 	);
57 
58 	put(sink, tag.span.headMutable);
59 }
60 
61 alias TaggableSymbols = AliasSeq!(
62 	AliasDeclaration,
63 	VarDeclaration,
64 	FuncDeclaration,
65 	EnumMember,
66 	VersionSymbol,
67 	StructDeclaration,
68 	// UnionDeclaration
69 	ClassDeclaration,
70 	// InterfaceDeclaration
71 	EnumDeclaration,
72 	TemplateDeclaration,
73 	Nspace,
74 	Module
75 );
76 
77 extern(C++) class SymbolTagger : SemanticTimeTransitiveVisitor
78 {
79 	import dmd.dsymbol: ScopeDsymbol, foreachDsymbol;
80 	import dmd.attrib: VisibilityDeclaration;
81 
82 	private Appender!(Span!(const(char)))* sink;
83 	private VisibilityDeclaration vd;
84 
85 	this(ref Appender!(Span!(const(char))) sink)
86 	{
87 		this.sink = &sink;
88 	}
89 
90 	alias visit = typeof(super).visit;
91 
92 	void visitMembers(ScopeDsymbol s)
93 	{
94 		s.members.foreachDsymbol((m) { if (m) m.accept(this); });
95 	}
96 
97 	/* Visibility information is not available via Dsymbol.visible prior to
98 	 * semantic analysis, so we have to keep track of visibility attributes
99 	 * while walking the parse tree.
100 	 */
101 	override void visit(VisibilityDeclaration innerVd)
102 	{
103 		auto outerVd = vd;
104 		vd = innerVd;
105 
106 		vd.decl.foreachDsymbol((d) { if (d) d.accept(this); });
107 
108 		vd = outerVd;
109 	}
110 
111 	static foreach (Symbol; TaggableSymbols) {
112 		override void visit(Symbol sym)
113 		{
114 			import dmd.dsymbol: Visibility;
115 
116 			bool isPrivate = vd && vd.visibility.kind == Visibility.Kind.private_;
117 			putTag(*sink, sym, isPrivate);
118 
119 			static if (is(Symbol : ScopeDsymbol))
120 				visitMembers(sym);
121 		}
122 	}
123 }
124 
125 Kind tagKind(Dsymbol sym)
126 {
127 	static extern(C++) class SymbolKindVisitor : Visitor
128 	{
129 		Kind result;
130 
131 		alias visit = typeof(super).visit;
132 
133 		override void visit(AliasDeclaration)
134 		{
135 			result = Kind.alias_;
136 		}
137 
138 		override void visit(ClassDeclaration)
139 		{
140 			result = Kind.class_;
141 		}
142 
143 		override void visit(EnumDeclaration)
144 		{
145 			result = Kind.enum_;
146 		}
147 
148 		override void visit(EnumMember)
149 		{
150 			result = Kind.enumMember;
151 		}
152 
153 		override void visit(FuncDeclaration)
154 		{
155 			result = Kind.function_;
156 		}
157 
158 		override void visit(InterfaceDeclaration)
159 		{
160 			result = Kind.interface_;
161 		}
162 
163 		override void visit(Module)
164 		{
165 			result = Kind.module_;
166 		}
167 
168 		override void visit(Nspace)
169 		{
170 			result = Kind.namespace;
171 		}
172 
173 		override void visit(StructDeclaration)
174 		{
175 			result = Kind.struct_;
176 		}
177 
178 		override void visit(TemplateDeclaration)
179 		{
180 			result = Kind.template_;
181 		}
182 
183 		override void visit(UnionDeclaration)
184 		{
185 			result = Kind.union_;
186 		}
187 
188 		override void visit(VarDeclaration vd)
189 		{
190 			if (vd.parent && vd.parent.isAggregateDeclaration)
191 				result = Kind.member;
192 			else
193 				result = Kind.variable;
194 		}
195 
196 		override void visit(VersionSymbol)
197 		{
198 			result = Kind.version_;
199 		}
200 	}
201 
202 	scope v = new SymbolKindVisitor();
203 	sym.accept(v);
204 	return v.result;
205 }