1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-30 21:55:31 +02:00

Bug 397496 - Formatter does not wrap long macro arguments

This commit is contained in:
Sergey Prigogin 2013-01-04 19:22:22 -08:00
parent fdbafed3f3
commit e2a18cf6c2
3 changed files with 208 additions and 133 deletions

View file

@ -418,6 +418,14 @@ public class MacroExpander {
return result;
}
private void addImageLocationInfo(int offset, Token t) {
ImageLocationInfo info= createImageLocationInfo(t);
if (info != null) {
info.fTokenOffsetInExpansion= offset;
fImageLocationInfos.add(info);
}
}
private ImageLocationInfo createImageLocationInfo(Token t) {
if (fLocationMap != null) {
final Object s= t.fSource;
@ -950,20 +958,12 @@ public class MacroExpander {
case CPreprocessor.tEXPANDED_IDENTIFIER:
t.setType(IToken.tIDENTIFIER);
if (createImageLocations) {
ImageLocationInfo info= createImageLocationInfo(t);
if (info != null) {
info.fTokenOffsetInExpansion= offset;
fImageLocationInfos.add(info);
}
addImageLocationInfo(offset, t);
}
break;
case IToken.tIDENTIFIER:
if (createImageLocations) {
ImageLocationInfo info= createImageLocationInfo(t);
if (info != null) {
info.fTokenOffsetInExpansion= offset;
fImageLocationInfos.add(info);
}
addImageLocationInfo(offset, t);
}
break;
@ -978,6 +978,12 @@ public class MacroExpander {
t.setOffset(offset, offset + t.getLength());
t.setNext(null);
return;
default:
if (createImageLocations && t.fSource instanceof CPreprocessor) {
addImageLocationInfo(offset, t);
}
break;
}
t.setOffset(offset, ++offset);
l= t;

View file

@ -66,6 +66,7 @@ import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTNullStatement;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
@ -143,6 +144,7 @@ import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator;
import org.eclipse.cdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.cdt.core.formatter.DefaultCodeFormatterOptions;
import org.eclipse.cdt.core.parser.IToken;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.formatter.align.Alignment;
import org.eclipse.cdt.internal.formatter.align.AlignmentException;
@ -232,6 +234,28 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
}
private static class TokenRange {
private int offset;
private int endOffset;
TokenRange(int offset, int endOffset) {
this.offset = offset;
this.endOffset = endOffset;
}
int getOffset() {
return offset;
}
int getEndOffset() {
return endOffset;
}
int getLength() {
return endOffset - offset;
}
}
/**
* Formats a trailing semicolon.
* @see #formatList(List, ListOptions, boolean, boolean, Runnable)
@ -360,6 +384,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
private final Scribe scribe;
private boolean fInsideFor;
private boolean fInsideMacroArguments;
private boolean fExpectSemicolonAfterDeclaration= true;
private MultiStatus fStatus;
@ -540,97 +565,79 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
if (fileLocation != null) {
scribe.printRaw(fileLocation.getNodeOffset(), fileLocation.getNodeLength());
}
fileLocation = macroExpansion.getFileLocation();
scribe.printNextToken(Token.tLPAREN);
IMacroBinding binding = (IMacroBinding) name.resolveBinding();
if (preferences.insert_space_after_opening_paren_in_method_invocation) {
scribe.space();
List<Object> arguments = getMacroArguments(binding.getParameterList().length);
final ListOptions options= new ListOptions(preferences.alignment_for_arguments_in_method_invocation);
options.fSeparatorToken = Token.tCOMMA;
options.fSpaceAfterOpeningParen= preferences.insert_space_after_opening_paren_in_method_invocation;
options.fSpaceBeforeClosingParen= preferences.insert_space_before_closing_paren_in_method_invocation;
options.fSpaceBetweenEmptyParen= preferences.insert_space_between_empty_parens_in_method_invocation;
options.fSpaceBeforeSeparator= preferences.insert_space_before_comma_in_method_invocation_arguments;
options.fSpaceAfterSeparator= preferences.insert_space_after_comma_in_method_invocation_arguments;
options.fTieBreakRule = Alignment.R_OUTERMOST;
fInsideMacroArguments = true;
try {
formatList(arguments, options, true, false, scribe.takeTailFormatter());
} finally {
fInsideMacroArguments = false;
}
final int continuationIndentation = preferences.continuation_indentation;
Alignment listAlignment = scribe.createAlignment(
Alignment.MACRO_ARGUMENTS,
preferences.alignment_for_arguments_in_method_invocation,
Alignment.R_OUTERMOST,
binding.getParameterList().length,
getCurrentPosition(),
continuationIndentation,
false);
scribe.enterAlignment(listAlignment);
boolean ok = false;
do {
try {
int fragment = 0;
scribe.alignFragment(listAlignment, fragment);
int parenLevel= 0;
boolean done = false;
while (!done) {
boolean hasWhitespace= scribe.printComment();
int token = peekNextToken();
switch (token) {
case Token.tLPAREN:
++parenLevel;
scribe.printNextToken(token, hasWhitespace);
break;
case Token.tRPAREN:
if (parenLevel > 0) {
--parenLevel;
scribe.printNextToken(token, hasWhitespace);
} else {
if (preferences.insert_space_before_closing_paren_in_method_invocation) {
scribe.space();
}
scribe.printNextToken(token);
done = true;
}
break;
case Token.tCOMMA:
if (parenLevel == 0 && preferences.insert_space_before_comma_in_method_invocation_arguments) {
scribe.space();
}
scribe.printNextToken(token);
if (parenLevel == 0) {
if (preferences.insert_space_after_comma_in_method_invocation_arguments) {
scribe.space();
}
scribe.printComment();
++fragment;
if (fragment < listAlignment.fragmentCount) {
scribe.alignFragment(listAlignment, fragment);
}
}
break;
case Token.tSTRING:
case Token.tLSTRING:
case Token.tRSTRING:
boolean needSpace= hasWhitespace;
while (true) {
scribe.printNextToken(token, needSpace);
if (peekNextToken() != token) {
break;
}
scribe.printCommentPreservingNewLines();
needSpace= true;
}
break;
case Token.tBADCHAR:
// Avoid infinite loop if something bad happened.
scribe.exitAlignment(listAlignment, true);
return;
default:
scribe.printNextToken(token, hasWhitespace);
}
}
/**
* Scans macro expansion arguments starting from the current position and returns a list of
* arguments where each argument is represented either by a {@link IASTNode} or, if not
* possible, by a {@link TokenRange}.
*/
private List<Object> getMacroArguments(int expectedNumberOfArguments) {
List<TokenRange> argumentRanges = new ArrayList<TokenRange>(expectedNumberOfArguments);
TokenRange currentArgument = null;
localScanner.resetTo(getCurrentPosition(), scribe.scannerEndPosition);
localScanner.getNextToken(); // Skip the opening parenthesis.
int parenLevel = 0;
int token;
while ((token = localScanner.getNextToken()) != Token.tBADCHAR) {
int tokenOffset = localScanner.getCurrentTokenStartPosition();
if (parenLevel == 0 && (token == Token.tCOMMA || token == Token.tRPAREN)) {
if (currentArgument != null) {
argumentRanges.add(currentArgument);
currentArgument = null;
} else {
argumentRanges.add(new TokenRange(tokenOffset, tokenOffset));
}
int token = peekNextToken();
if (token == Token.tSEMI) {
scribe.printNextToken(token);
scribe.startNewLine();
if (token == Token.tRPAREN)
break;
} else {
int tokenEndOffset = localScanner.getCurrentPosition();
if (currentArgument == null) {
currentArgument = new TokenRange(tokenOffset, tokenEndOffset);
} else {
currentArgument.endOffset = tokenEndOffset;
}
switch (token) {
case Token.tLPAREN:
++parenLevel;
break;
case Token.tRPAREN:
if (parenLevel > 0)
--parenLevel;
break;
}
ok = true;
} catch (AlignmentException e) {
scribe.redoAlignment(e);
}
} while (!ok);
scribe.exitAlignment(listAlignment, true);
}
List<Object> arguments = new ArrayList<Object>(argumentRanges.size());
IASTNodeSelector nodeSelector = ast.getNodeSelector(null);
for (TokenRange argument : argumentRanges) {
IASTNode node = nodeSelector.findNodeInExpansion(argument.getOffset(), argument.getLength());
if (node != null) {
arguments.add(node);
} else {
arguments.add(argument);
}
}
return arguments;
}
@Override
@ -2077,15 +2084,16 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
/**
* Format a given list of elements according alignment options.
*
* @param elements the elements to format
* @param elements the elements to format, which can be either {@link IASTNode}s or
* {@link TokenRange}s.
* @param options formatting options
* @param encloseInParen indicates whether the list should be enclosed in parentheses
* @param addEllipsis indicates whether ellipsis should be added after the last element
* @param tailFormatter formatter for the trailing text that should be kept together with
* the last element of the list.
*/
private void formatList(List<? extends IASTNode> elements, ListOptions options,
boolean encloseInParen, boolean addEllipsis, Runnable tailFormatter) {
private void formatList(List<?> elements, ListOptions options, boolean encloseInParen,
boolean addEllipsis, Runnable tailFormatter) {
if (encloseInParen)
scribe.printNextToken(Token.tLPAREN, options.fSpaceBeforeOpeningParen);
@ -2118,22 +2126,26 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
try {
int i;
for (i = 0; i < elementsLength; i++) {
final IASTNode node= elements.get(i);
final Object element = elements.get(i);
if (i < elementsLength - 1) {
scribe.setTailFormatter(
new TrailingTokenFormatter(options.fSeparatorToken,
findTokenAfterNode(options.fSeparatorToken, node),
findTokenAfterNodeOrTokenRange(options.fSeparatorToken, element),
options.fSpaceBeforeSeparator,
options.fSpaceAfterSeparator));
} else {
scribe.setTailFormatter(tailFormatter);
}
scribe.alignFragment(alignment, i);
if (node instanceof ICPPASTConstructorChainInitializer) {
// Constructor chain initializer is a special case.
visit((ICPPASTConstructorChainInitializer) node);
if (element instanceof IASTNode) {
if (element instanceof ICPPASTConstructorChainInitializer) {
// Constructor chain initializer is a special case.
visit((ICPPASTConstructorChainInitializer) element);
} else {
((IASTNode) element).accept(this);
}
} else {
node.accept(this);
formatTokenRange((TokenRange) element);
}
if (i < elementsLength - 1) {
scribe.runTailFormatter();
@ -2163,6 +2175,15 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
}
private void formatTokenRange(TokenRange tokenRange) {
scribe.restartAtOffset(tokenRange.getOffset());
while (getCurrentPosition() < tokenRange.getEndOffset()) {
boolean hasWhitespace= scribe.printComment();
int token = peekNextToken();
scribe.printNextToken(token, hasWhitespace);
}
}
private int visit(ICPPASTTryBlockStatement node) {
scribe.printNextToken(Token.t_try, scribe.printComment());
final IASTStatement tryBody= node.getTryBody();
@ -3782,18 +3803,23 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
IASTNodeLocation[] locations= node.getNodeLocations();
if (locations.length == 0) {
} else if (locations[0] instanceof IASTMacroExpansionLocation) {
} else if (!fInsideMacroArguments && locations[0] instanceof IASTMacroExpansionLocation) {
IASTMacroExpansionLocation location = (IASTMacroExpansionLocation) locations[0];
if (locations.length <= 2 && node instanceof IASTStatement) {
IASTPreprocessorMacroExpansion macroExpansion = location.getExpansion();
IASTFileLocation macroLocation = macroExpansion.getFileLocation();
IASTFileLocation nodeLocation = node.getFileLocation();
IASTFileLocation nodeLocation = getFileLocation(node);
if (macroLocation.getNodeOffset() >= getCurrentPosition() &&
!scribe.shouldSkip(macroLocation.getNodeOffset()) &&
(nodeLocation.getNodeOffset() + nodeLocation.getNodeLength() ==
macroLocation.getNodeOffset() + macroLocation.getNodeLength() ||
locations.length == 2 && isSemicolonLocation(locations[1])) &&
isFunctionStyleMacroExpansion(macroExpansion)) {
if (locations.length == 2 && isSemicolonLocation(locations[1])) {
scribe.setTailFormatter(
new TrailingTokenFormatter(Token.tSEMI, locations[1].getNodeOffset(),
preferences.insert_space_before_semicolon, false));
}
formatFunctionStyleMacroExpansion(macroExpansion);
return false;
}
@ -3808,12 +3834,16 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
return false;
}
} else {
IASTFileLocation fileLocation= node.getFileLocation();
IASTFileLocation fileLocation= getFileLocation(node);
scribe.restartAtOffset(fileLocation.getNodeOffset());
}
return true;
}
private IASTFileLocation getFileLocation(IASTNode node) {
return fInsideMacroArguments ? ((ASTNode) node).getImageLocation() : node.getFileLocation();
}
/**
* Formatting of node is complete. Undo skip region if any.
*
@ -3824,7 +3854,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
return;
}
if (scribe.skipRange()) {
IASTFileLocation fileLocation= node.getFileLocation();
IASTFileLocation fileLocation= getFileLocation(node);
if (fileLocation != null) {
int nodeEndOffset= fileLocation.getNodeOffset() + fileLocation.getNodeLength();
scribe.restartAtOffset(nodeEndOffset);
@ -3845,7 +3875,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
if (node instanceof IASTProblemHolder || node instanceof IASTTranslationUnit) {
return;
}
IASTFileLocation fileLocation= node.getFileLocation();
IASTFileLocation fileLocation= getFileLocation(node);
if (fileLocation == null) {
return;
}
@ -3881,7 +3911,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
private void skipNode(IASTNode node) {
final IASTNodeLocation fileLocation= node.getFileLocation();
final IASTNodeLocation fileLocation= getFileLocation(node);
if (fileLocation != null && fileLocation.getNodeLength() > 0) {
final int endOffset= fileLocation.getNodeOffset() + fileLocation.getNodeLength();
final int currentOffset= getCurrentPosition();
@ -3893,7 +3923,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
private void skipToNode(IASTNode node) {
final IASTNodeLocation fileLocation= node.getFileLocation();
final IASTNodeLocation fileLocation= getFileLocation(node);
if (fileLocation != null) {
final int startOffset= fileLocation.getNodeOffset();
final int currentOffset= getCurrentPosition();
@ -3905,7 +3935,7 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
private void skipNonWhitespaceToNode(IASTNode node) {
final IASTNodeLocation fileLocation= node.getFileLocation();
final IASTNodeLocation fileLocation= getFileLocation(node);
if (fileLocation != null) {
final int startOffset= fileLocation.getNodeOffset();
final int nextTokenOffset= getNextTokenOffset();
@ -3985,18 +4015,22 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
}
private static boolean startsWithMacroExpansion(IASTNode node) {
private boolean startsWithMacroExpansion(IASTNode node) {
if (fInsideMacroArguments)
return false;
IASTNodeLocation[] locations= node.getNodeLocations();
if (!(node instanceof IASTProblemHolder) && locations.length != 0 &&
locations[0] instanceof IASTMacroExpansionLocation) {
IASTFileLocation expansionLocation= locations[0].asFileLocation();
IASTFileLocation fileLocation= node.getFileLocation();
IASTFileLocation fileLocation= getFileLocation(node);
return expansionLocation.getNodeOffset() == fileLocation.getNodeOffset();
}
return false;
}
private static boolean endsWithMacroExpansion(IASTNode node) {
private boolean endsWithMacroExpansion(IASTNode node) {
if (fInsideMacroArguments)
return false;
IASTNodeLocation[] locations= node.getNodeLocations();
if (!(node instanceof IASTProblemHolder) && locations.length != 0 &&
locations[locations.length - 1] instanceof IASTMacroExpansionLocation) {
@ -4005,13 +4039,17 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
return false;
}
private static boolean enclosedInMacroExpansion(IASTNode node) {
private boolean enclosedInMacroExpansion(IASTNode node) {
if (fInsideMacroArguments)
return false;
IASTNodeLocation[] locations= node.getNodeLocations();
return locations.length == 1 && locations[0] instanceof IASTMacroExpansionLocation;
}
private static boolean withinMacroExpansion(IASTNode node, int offset) {
IASTFileLocation loc = node.getFileLocation();
private boolean withinMacroExpansion(IASTNode node, int offset) {
if (fInsideMacroArguments)
return false;
IASTFileLocation loc = getFileLocation(node);
if (loc == null || offset < loc.getNodeOffset() || offset >= loc.getNodeOffset() + loc.getNodeLength()) {
return false;
}
@ -4061,9 +4099,9 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
* normally separated by other tokens this is an indication that they were produced by the same
* macro expansion.
*/
private static boolean doNodeLocationsOverlap(IASTNode node1, IASTNode node2) {
IASTFileLocation loc1 = node1.getFileLocation();
IASTFileLocation loc2 = node2.getFileLocation();
private boolean doNodeLocationsOverlap(IASTNode node1, IASTNode node2) {
IASTFileLocation loc1 = getFileLocation(node1);
IASTFileLocation loc2 = getFileLocation(node2);
return loc1.getNodeOffset() + loc1.getNodeLength() > loc2.getNodeOffset() &&
loc1.getNodeOffset() < loc2.getNodeOffset() + loc2.getNodeLength();
}
@ -4073,16 +4111,16 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
* separated by other tokens this is an indication that they were produced by the same macro
* expansion.
*/
private static boolean doNodesHaveSameOffset(IASTNode node1, IASTNode node2) {
private boolean doNodesHaveSameOffset(IASTNode node1, IASTNode node2) {
return nodeOffset(node1) == nodeOffset(node2);
}
private static int nodeOffset(IASTNode node) {
return node.getFileLocation().getNodeOffset();
private int nodeOffset(IASTNode node) {
return getFileLocation(node).getNodeOffset();
}
private static int nodeEndOffset(IASTNode node) {
IASTFileLocation loc = node.getFileLocation();
private int nodeEndOffset(IASTNode node) {
IASTFileLocation loc = getFileLocation(node);
return loc.getNodeOffset() + loc.getNodeLength();
}
@ -4407,14 +4445,19 @@ public class CodeFormatterVisitor extends ASTVisitor implements ICPPASTVisitor,
}
private int findTokenWithinNode(int tokenType, IASTNode node) {
IASTFileLocation location = node.getFileLocation();
IASTFileLocation location = getFileLocation(node);
int endOffset = location.getNodeOffset() + location.getNodeLength();
return scribe.findToken(tokenType, endOffset);
}
private int findTokenAfterNode(int tokenType, IASTNode node) {
IASTFileLocation location = node.getFileLocation();
int startOffset = location.getNodeOffset() + location.getNodeLength();
private int findTokenAfterNodeOrTokenRange(int tokenType, Object nodeOrTokenRange) {
int startOffset;
if (nodeOrTokenRange instanceof IASTNode) {
IASTFileLocation location = getFileLocation((IASTNode) nodeOrTokenRange);
startOffset = location.getNodeOffset() + location.getNodeLength();
} else {
startOffset = ((TokenRange) nodeOrTokenRange).getEndOffset();
}
return scribe.findToken(tokenType, startOffset, scribe.scannerEndPosition - 1);
}
}

View file

@ -1765,7 +1765,6 @@ public class CodeFormatterTest extends BaseUITestCase {
// }
//#endif
//}
public void testMacroAsFunctionArguments_Bug253039() throws Exception {
assertFormatterResult();
}
@ -1901,6 +1900,33 @@ public class CodeFormatterTest extends BaseUITestCase {
assertFormatterResult();
}
//#define MACRO(a,b) f(a,b)
//void f(bool b, int i);
//int function_with_loooooooooooooooong_name();
//int another_function_with_loooooong_name();
//
//void test(){
// MACRO("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"=="bbbbbbbbbbbbbbbbbbbbbbbbbbb",function_with_loooooooooooooooong_name()+another_function_with_loooooong_name());
//}
//#define MACRO(a,b) f(a,b)
//void f(bool b, int i);
//int function_with_loooooooooooooooong_name();
//int another_function_with_loooooong_name();
//
//void test() {
// MACRO("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
// == "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
// function_with_loooooooooooooooong_name()
// + another_function_with_loooooong_name());
//}
public void testMacroArguments() throws Exception {
fOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, CCorePlugin.SPACE);
fOptions.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION,
Integer.toString(Alignment.M_COMPACT_SPLIT | Alignment.M_INDENT_ON_COLUMN));
assertFormatterResult();
}
//bool member __attribute__ ((__unused__)) = false;
//bool member __attribute__ ((__unused__)) = false;
@ -2892,7 +2918,7 @@ public class CodeFormatterTest extends BaseUITestCase {
//void f() {
// if (1) {
// }
// IF(1>0);
// IF(1 > 0);
//}
public void testMacroAfterCompoundStatement_Bug356690() throws Exception {
assertFormatterResult();