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