498 lines
16 KiB
D
498 lines
16 KiB
D
// Copyright Brian Schott 2015.
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
module dfmt.ast_info;
|
|
|
|
import dparse.lexer;
|
|
import dparse.ast;
|
|
|
|
enum BraceIndentInfoFlags
|
|
{
|
|
tempIndent = 1 << 0,
|
|
}
|
|
|
|
struct BraceIndentInfo
|
|
{
|
|
size_t startLocation;
|
|
size_t endLocation;
|
|
|
|
uint flags;
|
|
|
|
uint beginIndentLevel;
|
|
}
|
|
|
|
struct StructInitializerInfo
|
|
{
|
|
size_t startLocation;
|
|
size_t endLocation;
|
|
}
|
|
|
|
/// AST information that is needed by the formatter.
|
|
struct ASTInformation
|
|
{
|
|
/// Sorts the arrays so that binary search will work on them
|
|
void cleanup()
|
|
{
|
|
import std.algorithm : sort, uniq;
|
|
import std.array : array;
|
|
|
|
sort(doubleNewlineLocations);
|
|
sort(spaceAfterLocations);
|
|
sort(unaryLocations);
|
|
sort(attributeDeclarationLines);
|
|
sort(atAttributeStartLocations);
|
|
sort(caseEndLocations);
|
|
sort(structInitStartLocations);
|
|
sort(structInitEndLocations);
|
|
sort(funLitStartLocations);
|
|
sort(funLitEndLocations);
|
|
sort(conditionalWithElseLocations);
|
|
sort(conditionalStatementLocations);
|
|
sort(arrayStartLocations);
|
|
sort(assocArrayStartLocations);
|
|
sort(contractLocations);
|
|
sort(constraintLocations);
|
|
sort(constructorDestructorLocations);
|
|
sort(staticConstructorDestructorLocations);
|
|
sort(sharedStaticConstructorDestructorLocations);
|
|
sort!((a,b) => a.endLocation < b.endLocation)
|
|
(indentInfoSortedByEndLocation);
|
|
sort!((a,b) => a.endLocation < b.endLocation)
|
|
(structInfoSortedByEndLocation);
|
|
sort(ufcsHintLocations);
|
|
ufcsHintLocations = ufcsHintLocations.uniq().array();
|
|
sort(ternaryColonLocations);
|
|
sort(namedArgumentColonLocations);
|
|
}
|
|
|
|
/// Locations of end braces for struct bodies
|
|
size_t[] doubleNewlineLocations;
|
|
|
|
/// Locations of tokens where a space is needed (such as the '*' in a type)
|
|
size_t[] spaceAfterLocations;
|
|
|
|
/// Locations of unary operators
|
|
size_t[] unaryLocations;
|
|
|
|
/// Lines containing attribute declarations
|
|
size_t[] attributeDeclarationLines;
|
|
|
|
/// Lines containing attribute declarations that can be followed by a new line
|
|
size_t[] atAttributeStartLocations;
|
|
|
|
/// Case statement colon locations
|
|
size_t[] caseEndLocations;
|
|
|
|
/// Opening braces of struct initializers
|
|
size_t[] structInitStartLocations;
|
|
|
|
/// Closing braces of struct initializers
|
|
size_t[] structInitEndLocations;
|
|
|
|
/// Opening braces of function literals
|
|
size_t[] funLitStartLocations;
|
|
|
|
/// Closing braces of function literals
|
|
size_t[] funLitEndLocations;
|
|
|
|
/// Locations of aggregate bodies (struct, class, union)
|
|
size_t[] aggregateBodyLocations;
|
|
|
|
/// Locations of function bodies
|
|
size_t[] funBodyLocations;
|
|
|
|
/// Conditional statements that have matching "else" statements
|
|
size_t[] conditionalWithElseLocations;
|
|
|
|
/// Conditional statement locations
|
|
size_t[] conditionalStatementLocations;
|
|
|
|
/// Locations of start locations of array initializers
|
|
size_t[] arrayStartLocations;
|
|
|
|
/// Locations of start locations of associative array initializers
|
|
size_t[] assocArrayStartLocations;
|
|
|
|
/// Locations of "in" and "out" tokens that begin contracts
|
|
size_t[] contractLocations;
|
|
|
|
/// Locations of template constraint "if" tokens
|
|
size_t[] constraintLocations;
|
|
|
|
/// Locations of constructor/destructor "shared" tokens ?
|
|
size_t[] sharedStaticConstructorDestructorLocations;
|
|
|
|
/// Locations of constructor/destructor "static" tokens ?
|
|
size_t[] staticConstructorDestructorLocations;
|
|
|
|
/// Locations of constructor/destructor "this" tokens ?
|
|
size_t[] constructorDestructorLocations;
|
|
|
|
/// Locations of '.' characters that might be UFCS chains.
|
|
size_t[] ufcsHintLocations;
|
|
|
|
BraceIndentInfo[] indentInfoSortedByEndLocation;
|
|
|
|
/// Opening & closing braces of struct initializers
|
|
StructInitializerInfo[] structInfoSortedByEndLocation;
|
|
|
|
/// Locations ternary expression colons.
|
|
size_t[] ternaryColonLocations;
|
|
|
|
/// Locations of named arguments of function call or struct constructor.
|
|
size_t[] namedArgumentColonLocations;
|
|
}
|
|
|
|
/// Collects information from the AST that is useful for the formatter
|
|
final class FormatVisitor : ASTVisitor
|
|
{
|
|
alias visit = ASTVisitor.visit;
|
|
|
|
/**
|
|
* Params:
|
|
* astInformation = the AST information that will be filled in
|
|
*/
|
|
this(ASTInformation* astInformation)
|
|
{
|
|
this.astInformation = astInformation;
|
|
}
|
|
|
|
override void visit(const ArrayInitializer arrayInitializer)
|
|
{
|
|
astInformation.arrayStartLocations ~= arrayInitializer.startLocation;
|
|
arrayInitializer.accept(this);
|
|
}
|
|
|
|
override void visit(const ArrayLiteral arrayLiteral)
|
|
{
|
|
astInformation.arrayStartLocations ~= arrayLiteral.tokens[0].index;
|
|
arrayLiteral.accept(this);
|
|
}
|
|
|
|
override void visit(const AssocArrayLiteral assocArrayLiteral)
|
|
{
|
|
astInformation.arrayStartLocations ~= assocArrayLiteral.tokens[0].index;
|
|
astInformation.assocArrayStartLocations ~= assocArrayLiteral.tokens[0].index;
|
|
assocArrayLiteral.accept(this);
|
|
}
|
|
|
|
override void visit (const SharedStaticConstructor sharedStaticConstructor)
|
|
{
|
|
astInformation.sharedStaticConstructorDestructorLocations ~= sharedStaticConstructor.location;
|
|
sharedStaticConstructor.accept(this);
|
|
}
|
|
|
|
override void visit (const SharedStaticDestructor sharedStaticDestructor)
|
|
{
|
|
astInformation.sharedStaticConstructorDestructorLocations ~= sharedStaticDestructor.location;
|
|
sharedStaticDestructor.accept(this);
|
|
}
|
|
|
|
override void visit (const StaticConstructor staticConstructor)
|
|
{
|
|
astInformation.staticConstructorDestructorLocations ~= staticConstructor.location;
|
|
staticConstructor.accept(this);
|
|
}
|
|
|
|
override void visit (const StaticDestructor staticDestructor)
|
|
{
|
|
astInformation.staticConstructorDestructorLocations ~= staticDestructor.location;
|
|
staticDestructor.accept(this);
|
|
}
|
|
|
|
override void visit (const Constructor constructor)
|
|
{
|
|
astInformation.constructorDestructorLocations ~= constructor.location;
|
|
constructor.accept(this);
|
|
}
|
|
|
|
override void visit (const Destructor destructor)
|
|
{
|
|
astInformation.constructorDestructorLocations ~= destructor.index;
|
|
destructor.accept(this);
|
|
}
|
|
|
|
override void visit (const FunctionBody functionBody)
|
|
{
|
|
if (auto bd = functionBody.specifiedFunctionBody)
|
|
{
|
|
if (bd.blockStatement)
|
|
{
|
|
astInformation.funBodyLocations ~= bd.blockStatement.startLocation;
|
|
}
|
|
}
|
|
functionBody.accept(this);
|
|
}
|
|
|
|
override void visit(const ConditionalDeclaration dec)
|
|
{
|
|
if (dec.hasElse)
|
|
{
|
|
auto condition = dec.compileCondition;
|
|
if (condition.versionCondition !is null)
|
|
{
|
|
astInformation.conditionalWithElseLocations
|
|
~= condition.versionCondition.versionIndex;
|
|
}
|
|
else if (condition.debugCondition !is null)
|
|
{
|
|
astInformation.conditionalWithElseLocations ~= condition.debugCondition.debugIndex;
|
|
}
|
|
// Skip "static if" because the formatting for normal "if" handles
|
|
// it properly
|
|
}
|
|
dec.accept(this);
|
|
}
|
|
|
|
override void visit(const Constraint constraint)
|
|
{
|
|
astInformation.constraintLocations ~= constraint.location;
|
|
constraint.accept(this);
|
|
}
|
|
|
|
override void visit(const ConditionalStatement statement)
|
|
{
|
|
auto condition = statement.compileCondition;
|
|
if (condition.versionCondition !is null)
|
|
{
|
|
astInformation.conditionalStatementLocations ~= condition.versionCondition.versionIndex;
|
|
}
|
|
else if (condition.debugCondition !is null)
|
|
{
|
|
astInformation.conditionalStatementLocations ~= condition.debugCondition.debugIndex;
|
|
}
|
|
statement.accept(this);
|
|
}
|
|
|
|
override void visit(const FunctionLiteralExpression funcLit)
|
|
{
|
|
if (funcLit.specifiedFunctionBody !is null)
|
|
{
|
|
const bs = funcLit.specifiedFunctionBody.blockStatement;
|
|
|
|
astInformation.funLitStartLocations ~= bs.startLocation;
|
|
astInformation.funLitEndLocations ~= bs.endLocation;
|
|
astInformation.indentInfoSortedByEndLocation ~=
|
|
BraceIndentInfo(bs.startLocation, bs.endLocation);
|
|
}
|
|
funcLit.accept(this);
|
|
}
|
|
|
|
override void visit(const DefaultStatement defaultStatement)
|
|
{
|
|
astInformation.caseEndLocations ~= defaultStatement.colonLocation;
|
|
defaultStatement.accept(this);
|
|
}
|
|
|
|
override void visit(const CaseStatement caseStatement)
|
|
{
|
|
astInformation.caseEndLocations ~= caseStatement.colonLocation;
|
|
caseStatement.accept(this);
|
|
}
|
|
|
|
override void visit(const CaseRangeStatement caseRangeStatement)
|
|
{
|
|
astInformation.caseEndLocations ~= caseRangeStatement.colonLocation;
|
|
caseRangeStatement.accept(this);
|
|
}
|
|
|
|
override void visit(const SpecifiedFunctionBody specifiedFunctionBody)
|
|
{
|
|
if (specifiedFunctionBody.blockStatement !is null)
|
|
astInformation.doubleNewlineLocations ~= specifiedFunctionBody.blockStatement.endLocation;
|
|
specifiedFunctionBody.accept(this);
|
|
}
|
|
|
|
override void visit(const StructInitializer structInitializer)
|
|
{
|
|
astInformation.structInitStartLocations ~= structInitializer.startLocation;
|
|
astInformation.structInitEndLocations ~= structInitializer.endLocation;
|
|
astInformation.structInfoSortedByEndLocation ~=
|
|
StructInitializerInfo(structInitializer.startLocation, structInitializer.endLocation);
|
|
astInformation.indentInfoSortedByEndLocation ~=
|
|
BraceIndentInfo(structInitializer.startLocation, structInitializer.endLocation);
|
|
|
|
structInitializer.accept(this);
|
|
}
|
|
|
|
override void visit(const EnumBody enumBody)
|
|
{
|
|
astInformation.doubleNewlineLocations ~= enumBody.endLocation;
|
|
enumBody.accept(this);
|
|
}
|
|
|
|
override void visit(const Unittest unittest_)
|
|
{
|
|
astInformation.doubleNewlineLocations ~= unittest_.blockStatement.endLocation;
|
|
unittest_.accept(this);
|
|
}
|
|
|
|
override void visit(const Invariant invariant_)
|
|
{
|
|
if (invariant_.blockStatement !is null)
|
|
astInformation.doubleNewlineLocations ~= invariant_.blockStatement.endLocation;
|
|
|
|
invariant_.accept(this);
|
|
}
|
|
|
|
override void visit(const StructBody structBody)
|
|
{
|
|
astInformation.aggregateBodyLocations ~= structBody.startLocation;
|
|
astInformation.doubleNewlineLocations ~= structBody.endLocation;
|
|
structBody.accept(this);
|
|
}
|
|
|
|
override void visit(const TemplateDeclaration templateDeclaration)
|
|
{
|
|
astInformation.doubleNewlineLocations ~= templateDeclaration.endLocation;
|
|
templateDeclaration.accept(this);
|
|
}
|
|
|
|
override void visit(const TypeSuffix typeSuffix)
|
|
{
|
|
if (typeSuffix.star.type != tok!"")
|
|
astInformation.spaceAfterLocations ~= typeSuffix.star.index;
|
|
typeSuffix.accept(this);
|
|
}
|
|
|
|
override void visit(const UnaryExpression unary)
|
|
{
|
|
import std.typecons : rebindable;
|
|
|
|
int chainLength;
|
|
auto u = rebindable(unary);
|
|
while (u !is null)
|
|
{
|
|
if (u.identifierOrTemplateInstance !is null
|
|
&& u.identifierOrTemplateInstance.templateInstance !is null)
|
|
chainLength++;
|
|
u = u.unaryExpression;
|
|
}
|
|
if (chainLength > 1)
|
|
{
|
|
u = unary;
|
|
while (u.unaryExpression !is null)
|
|
{
|
|
astInformation.ufcsHintLocations ~= u.dotLocation;
|
|
u = u.unaryExpression;
|
|
}
|
|
}
|
|
if (unary.prefix.type == tok!"~" || unary.prefix.type == tok!"&"
|
|
|| unary.prefix.type == tok!"*"
|
|
|| unary.prefix.type == tok!"+" || unary.prefix.type == tok!"-")
|
|
{
|
|
astInformation.unaryLocations ~= unary.prefix.index;
|
|
}
|
|
unary.accept(this);
|
|
}
|
|
|
|
override void visit(const AttributeDeclaration attributeDeclaration)
|
|
{
|
|
astInformation.attributeDeclarationLines ~= attributeDeclaration.line;
|
|
attributeDeclaration.accept(this);
|
|
}
|
|
|
|
override void visit(const FunctionAttribute functionAttribute)
|
|
{
|
|
if (functionAttribute.atAttribute !is null)
|
|
astInformation.atAttributeStartLocations ~= functionAttribute.atAttribute.startLocation;
|
|
functionAttribute.accept(this);
|
|
}
|
|
|
|
override void visit(const MemberFunctionAttribute memberFunctionAttribute)
|
|
{
|
|
if (memberFunctionAttribute.atAttribute !is null)
|
|
astInformation.atAttributeStartLocations ~= memberFunctionAttribute.atAttribute.startLocation;
|
|
memberFunctionAttribute.accept(this);
|
|
}
|
|
|
|
override void visit(const Attribute attribute)
|
|
{
|
|
if (attribute.atAttribute !is null)
|
|
astInformation.atAttributeStartLocations ~= attribute.atAttribute.startLocation;
|
|
attribute.accept(this);
|
|
}
|
|
|
|
override void visit(const StorageClass storageClass)
|
|
{
|
|
if (storageClass.atAttribute !is null)
|
|
astInformation.atAttributeStartLocations ~= storageClass.atAttribute.startLocation;
|
|
storageClass.accept(this);
|
|
}
|
|
|
|
override void visit(const InContractExpression inContractExpression)
|
|
{
|
|
astInformation.contractLocations ~= inContractExpression.inTokenLocation;
|
|
inContractExpression.accept(this);
|
|
}
|
|
|
|
override void visit(const InStatement inStatement)
|
|
{
|
|
astInformation.contractLocations ~= inStatement.inTokenLocation;
|
|
inStatement.accept(this);
|
|
}
|
|
|
|
override void visit(const OutContractExpression outContractExpression)
|
|
{
|
|
astInformation.contractLocations ~= outContractExpression.outTokenLocation;
|
|
outContractExpression.accept(this);
|
|
}
|
|
|
|
override void visit(const OutStatement outStatement)
|
|
{
|
|
astInformation.contractLocations ~= outStatement.outTokenLocation;
|
|
outStatement.accept(this);
|
|
}
|
|
|
|
override void visit(const TernaryExpression ternaryExpression)
|
|
{
|
|
astInformation.ternaryColonLocations ~= ternaryExpression.colon.index;
|
|
ternaryExpression.accept(this);
|
|
}
|
|
|
|
override void visit(const FunctionCallExpression functionCall)
|
|
{
|
|
// Check if function has any arguments.
|
|
if (functionCall.arguments.namedArgumentList is null)
|
|
{
|
|
functionCall.accept(this);
|
|
return;
|
|
}
|
|
|
|
/+
|
|
Items are function arguments: f(<item>, <item>);
|
|
Iterate them and check if they are named arguments: tok!":" belongs to a
|
|
named argument if it is preceeded by one tok!"identifier" (+ any number
|
|
of comments):
|
|
+/
|
|
foreach (item; functionCall.arguments.namedArgumentList.items)
|
|
{
|
|
// Set to true after first tok!"identifier".
|
|
auto foundIdentifier = false;
|
|
|
|
foreach (t; item.tokens)
|
|
{
|
|
if (t.type == tok!"identifier" && !foundIdentifier)
|
|
{
|
|
foundIdentifier = true;
|
|
continue;
|
|
}
|
|
|
|
if (t.type == tok!":" && foundIdentifier)
|
|
{
|
|
astInformation.namedArgumentColonLocations ~= t.index;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
functionCall.accept(this);
|
|
}
|
|
|
|
private:
|
|
ASTInformation* astInformation;
|
|
}
|