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 }