1 //          Copyright Brian Schott 2015.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module dfmt.ast_info;
7 
8 import dparse.lexer;
9 import dparse.ast;
10 
11 enum BraceIndentInfoFlags
12 {
13     tempIndent = 1 << 0,
14 }
15 
16 struct BraceIndentInfo
17 {
18     size_t startLocation;
19     size_t endLocation;
20 
21     uint flags;
22 
23     uint beginIndentLevel;
24 }
25 
26 struct StructInitializerInfo
27 {
28     size_t startLocation;
29     size_t endLocation;
30 }
31 
32 /// AST information that is needed by the formatter.
33 struct ASTInformation
34 {
35     /// Sorts the arrays so that binary search will work on them
36     void cleanup()
37     {
38         import std.algorithm : sort, uniq;
39         import std.array : array;
40 
41         sort(doubleNewlineLocations);
42         sort(spaceAfterLocations);
43         sort(unaryLocations);
44         sort(attributeDeclarationLines);
45         sort(atAttributeStartLocations);
46         sort(caseEndLocations);
47         sort(structInitStartLocations);
48         sort(structInitEndLocations);
49         sort(funLitStartLocations);
50         sort(funLitEndLocations);
51         sort(conditionalWithElseLocations);
52         sort(conditionalStatementLocations);
53         sort(arrayStartLocations);
54         sort(assocArrayStartLocations);
55         sort(contractLocations);
56         sort(constraintLocations);
57         sort(constructorDestructorLocations);
58         sort(staticConstructorDestructorLocations);
59         sort(sharedStaticConstructorDestructorLocations);
60         sort!((a,b) => a.endLocation < b.endLocation)
61             (indentInfoSortedByEndLocation);
62         sort!((a,b) => a.endLocation < b.endLocation)
63             (structInfoSortedByEndLocation);
64         sort(ufcsHintLocations);
65         ufcsHintLocations = ufcsHintLocations.uniq().array();
66     }
67 
68     /// Locations of end braces for struct bodies
69     size_t[] doubleNewlineLocations;
70 
71     /// Locations of tokens where a space is needed (such as the '*' in a type)
72     size_t[] spaceAfterLocations;
73 
74     /// Locations of unary operators
75     size_t[] unaryLocations;
76 
77     /// Lines containing attribute declarations
78     size_t[] attributeDeclarationLines;
79 
80     /// Lines containing attribute declarations that can be followed by a new line
81     size_t[] atAttributeStartLocations;
82 
83     /// Case statement colon locations
84     size_t[] caseEndLocations;
85 
86     /// Opening braces of struct initializers
87     size_t[] structInitStartLocations;
88 
89     /// Closing braces of struct initializers
90     size_t[] structInitEndLocations;
91 
92     /// Opening braces of function literals
93     size_t[] funLitStartLocations;
94 
95     /// Closing braces of function literals
96     size_t[] funLitEndLocations;
97 
98     /// Conditional statements that have matching "else" statements
99     size_t[] conditionalWithElseLocations;
100 
101     /// Conditional statement locations
102     size_t[] conditionalStatementLocations;
103 
104     /// Locations of start locations of array initializers
105     size_t[] arrayStartLocations;
106 
107     /// Locations of start locations of associative array initializers
108     size_t[] assocArrayStartLocations;
109 
110     /// Locations of "in" and "out" tokens that begin contracts
111     size_t[] contractLocations;
112 
113     /// Locations of template constraint "if" tokens
114     size_t[] constraintLocations;
115 
116     /// Locations of constructor/destructor "shared" tokens ?
117     size_t[] sharedStaticConstructorDestructorLocations;
118 
119     /// Locations of constructor/destructor "static" tokens ?
120     size_t[] staticConstructorDestructorLocations;
121 
122     /// Locations of constructor/destructor "this" tokens ?
123     size_t[] constructorDestructorLocations;
124 
125     /// Locations of '.' characters that might be UFCS chains.
126     size_t[] ufcsHintLocations;
127 
128     BraceIndentInfo[] indentInfoSortedByEndLocation;
129 
130     /// Opening & closing braces of struct initializers
131     StructInitializerInfo[] structInfoSortedByEndLocation;
132 }
133 
134 /// Collects information from the AST that is useful for the formatter
135 final class FormatVisitor : ASTVisitor
136 {
137     alias visit = ASTVisitor.visit;
138 
139     /**
140      * Params:
141      *     astInformation = the AST information that will be filled in
142      */
143     this(ASTInformation* astInformation)
144     {
145         this.astInformation = astInformation;
146     }
147 
148     override void visit(const ArrayInitializer arrayInitializer)
149     {
150         astInformation.arrayStartLocations ~= arrayInitializer.startLocation;
151         arrayInitializer.accept(this);
152     }
153 
154     override void visit(const ArrayLiteral arrayLiteral)
155     {
156         astInformation.arrayStartLocations ~= arrayLiteral.tokens[0].index;
157         arrayLiteral.accept(this);
158     }
159 
160     override void visit(const AssocArrayLiteral assocArrayLiteral)
161     {
162         astInformation.arrayStartLocations ~= assocArrayLiteral.tokens[0].index;
163         astInformation.assocArrayStartLocations ~= assocArrayLiteral.tokens[0].index;
164         assocArrayLiteral.accept(this);
165     }
166 
167     override void visit (const SharedStaticConstructor sharedStaticConstructor)
168     {
169         astInformation.sharedStaticConstructorDestructorLocations ~= sharedStaticConstructor.location;
170         sharedStaticConstructor.accept(this);
171     }
172 
173     override void visit (const SharedStaticDestructor sharedStaticDestructor)
174     {
175         astInformation.sharedStaticConstructorDestructorLocations ~= sharedStaticDestructor.location;
176         sharedStaticDestructor.accept(this);
177     }
178 
179     override void visit (const StaticConstructor staticConstructor)
180     {
181         astInformation.staticConstructorDestructorLocations ~= staticConstructor.location;
182         staticConstructor.accept(this);
183     }
184 
185     override void visit (const StaticDestructor staticDestructor)
186     {
187         astInformation.staticConstructorDestructorLocations ~= staticDestructor.location;
188         staticDestructor.accept(this);
189     }
190 
191     override void visit (const Constructor constructor)
192     {
193         astInformation.constructorDestructorLocations ~= constructor.location;
194         constructor.accept(this);
195     }
196 
197     override void visit (const Destructor destructor)
198     {
199         astInformation.constructorDestructorLocations ~= destructor.index;
200         destructor.accept(this);
201     }
202 
203     override void visit(const ConditionalDeclaration dec)
204     {
205         if (dec.hasElse)
206         {
207             auto condition = dec.compileCondition;
208             if (condition.versionCondition !is null)
209             {
210                 astInformation.conditionalWithElseLocations
211                     ~= condition.versionCondition.versionIndex;
212             }
213             else if (condition.debugCondition !is null)
214             {
215                 astInformation.conditionalWithElseLocations ~= condition.debugCondition.debugIndex;
216             }
217             // Skip "static if" because the formatting for normal "if" handles
218             // it properly
219         }
220         dec.accept(this);
221     }
222 
223     override void visit(const Constraint constraint)
224     {
225         astInformation.constraintLocations ~= constraint.location;
226         constraint.accept(this);
227     }
228 
229     override void visit(const ConditionalStatement statement)
230     {
231         auto condition = statement.compileCondition;
232         if (condition.versionCondition !is null)
233         {
234             astInformation.conditionalStatementLocations ~= condition.versionCondition.versionIndex;
235         }
236         else if (condition.debugCondition !is null)
237         {
238             astInformation.conditionalStatementLocations ~= condition.debugCondition.debugIndex;
239         }
240         statement.accept(this);
241     }
242 
243     override void visit(const FunctionLiteralExpression funcLit)
244     {
245         if (funcLit.specifiedFunctionBody !is null)
246         {
247             const bs = funcLit.specifiedFunctionBody.blockStatement;
248 
249             astInformation.funLitStartLocations ~= bs.startLocation;
250             astInformation.funLitEndLocations ~= bs.endLocation;
251             astInformation.indentInfoSortedByEndLocation ~=
252                 BraceIndentInfo(bs.startLocation, bs.endLocation);
253         }
254         funcLit.accept(this);
255     }
256 
257     override void visit(const DefaultStatement defaultStatement)
258     {
259         astInformation.caseEndLocations ~= defaultStatement.colonLocation;
260         defaultStatement.accept(this);
261     }
262 
263     override void visit(const CaseStatement caseStatement)
264     {
265         astInformation.caseEndLocations ~= caseStatement.colonLocation;
266         caseStatement.accept(this);
267     }
268 
269     override void visit(const CaseRangeStatement caseRangeStatement)
270     {
271         astInformation.caseEndLocations ~= caseRangeStatement.colonLocation;
272         caseRangeStatement.accept(this);
273     }
274 
275     override void visit(const SpecifiedFunctionBody specifiedFunctionBody)
276     {
277         if (specifiedFunctionBody.blockStatement !is null)
278             astInformation.doubleNewlineLocations ~= specifiedFunctionBody.blockStatement.endLocation;
279         specifiedFunctionBody.accept(this);
280     }
281 
282     override void visit(const StructInitializer structInitializer)
283     {
284         astInformation.structInitStartLocations ~= structInitializer.startLocation;
285         astInformation.structInitEndLocations ~= structInitializer.endLocation;
286         astInformation.structInfoSortedByEndLocation ~=
287             StructInitializerInfo(structInitializer.startLocation, structInitializer.endLocation);
288         astInformation.indentInfoSortedByEndLocation ~=
289             BraceIndentInfo(structInitializer.startLocation, structInitializer.endLocation);
290 
291         structInitializer.accept(this);
292     }
293 
294     override void visit(const EnumBody enumBody)
295     {
296         astInformation.doubleNewlineLocations ~= enumBody.endLocation;
297         enumBody.accept(this);
298     }
299 
300     override void visit(const Unittest unittest_)
301     {
302         astInformation.doubleNewlineLocations ~= unittest_.blockStatement.endLocation;
303         unittest_.accept(this);
304     }
305 
306     override void visit(const Invariant invariant_)
307     {
308         if (invariant_.blockStatement !is null)
309             astInformation.doubleNewlineLocations ~= invariant_.blockStatement.endLocation;
310         
311         invariant_.accept(this);
312     }
313 
314     override void visit(const StructBody structBody)
315     {
316         astInformation.doubleNewlineLocations ~= structBody.endLocation;
317         structBody.accept(this);
318     }
319 
320     override void visit(const TemplateDeclaration templateDeclaration)
321     {
322         astInformation.doubleNewlineLocations ~= templateDeclaration.endLocation;
323         templateDeclaration.accept(this);
324     }
325 
326     override void visit(const TypeSuffix typeSuffix)
327     {
328         if (typeSuffix.star.type != tok!"")
329             astInformation.spaceAfterLocations ~= typeSuffix.star.index;
330         typeSuffix.accept(this);
331     }
332 
333     override void visit(const UnaryExpression unary)
334     {
335         import std.typecons : rebindable;
336 
337         int chainLength;
338         auto u = rebindable(unary);
339         while (u !is null)
340         {
341             if (u.identifierOrTemplateInstance !is null
342                     && u.identifierOrTemplateInstance.templateInstance !is null)
343                 chainLength++;
344             u = u.unaryExpression;
345         }
346         if (chainLength > 1)
347         {
348             u = unary;
349             while (u.unaryExpression !is null)
350             {
351                 astInformation.ufcsHintLocations ~= u.dotLocation;
352                 u = u.unaryExpression;
353             }
354         }
355         if (unary.prefix.type == tok!"~" || unary.prefix.type == tok!"&"
356                 || unary.prefix.type == tok!"*"
357                 || unary.prefix.type == tok!"+" || unary.prefix.type == tok!"-")
358         {
359             astInformation.unaryLocations ~= unary.prefix.index;
360         }
361         unary.accept(this);
362     }
363 
364     override void visit(const AttributeDeclaration attributeDeclaration)
365     {
366         astInformation.attributeDeclarationLines ~= attributeDeclaration.line;
367         attributeDeclaration.accept(this);
368     }
369 
370     override void visit(const FunctionAttribute functionAttribute)
371     {
372         if (functionAttribute.atAttribute !is null)
373             astInformation.atAttributeStartLocations ~= functionAttribute.atAttribute.startLocation;
374         functionAttribute.accept(this);
375     }
376 
377     override void visit(const MemberFunctionAttribute memberFunctionAttribute)
378     {
379         if (memberFunctionAttribute.atAttribute !is null)
380             astInformation.atAttributeStartLocations ~= memberFunctionAttribute.atAttribute.startLocation;
381         memberFunctionAttribute.accept(this);
382     }
383 
384     override void visit(const Attribute attribute)
385     {
386         if (attribute.atAttribute !is null)
387             astInformation.atAttributeStartLocations ~= attribute.atAttribute.startLocation;
388         attribute.accept(this);
389     }
390 
391     override void visit(const StorageClass storageClass)
392     {
393         if (storageClass.atAttribute !is null)
394             astInformation.atAttributeStartLocations ~= storageClass.atAttribute.startLocation;
395         storageClass.accept(this);
396     }
397 
398     override void visit(const InContractExpression inContractExpression)
399     {
400         astInformation.contractLocations ~= inContractExpression.inTokenLocation;
401         inContractExpression.accept(this);
402     }
403 
404     override void visit(const InStatement inStatement)
405     {
406         astInformation.contractLocations ~= inStatement.inTokenLocation;
407         inStatement.accept(this);
408     }
409 
410     override void visit(const OutContractExpression outContractExpression)
411     {
412         astInformation.contractLocations ~= outContractExpression.outTokenLocation;
413         outContractExpression.accept(this);
414     }
415 
416     override void visit(const OutStatement outStatement)
417     {
418         astInformation.contractLocations ~= outStatement.outTokenLocation;
419         outStatement.accept(this);
420     }
421 
422 private:
423     ASTInformation* astInformation;
424 }