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 }