1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-13 03:05:39 +02:00

Patch for Devin Steffler.

Fixed 39688 [Scanner] Macros with variable number of arguments are not supported (C99) (GCC)
- added support for C99 syntax for macros with variable arguments i.e. "..."
- added support for GCC syntax for macros with variable arguments i.e. "args..."
- added test cases for the above
This commit is contained in:
John Camelon 2004-10-20 17:59:54 +00:00
parent a22cca2f6b
commit f479e84d8f
8 changed files with 377 additions and 35 deletions

View file

@ -16,6 +16,7 @@ import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.parser.IProblem; import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.ISourceElementRequestor; import org.eclipse.cdt.core.parser.ISourceElementRequestor;
@ -26,6 +27,7 @@ import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.core.parser.ParserMode; import org.eclipse.cdt.core.parser.ParserMode;
import org.eclipse.cdt.core.parser.ast.IASTInclusion; import org.eclipse.cdt.core.parser.ast.IASTInclusion;
import org.eclipse.cdt.internal.core.parser.QuickParseCallback; import org.eclipse.cdt.internal.core.parser.QuickParseCallback;
import org.eclipse.cdt.internal.core.parser.scanner2.FunctionStyleMacro;
/** /**
* @author jcamelon * @author jcamelon
@ -2002,4 +2004,180 @@ public class Scanner2Test extends BaseScanner2Test
assertEquals( 0, callback.problems.size() ); assertEquals( 0, callback.problems.size() );
} }
public void testBug39688A() throws Exception { // test valid IProblems
Writer writer = new StringWriter();
writer.write("#define decl1(type, ... \\\n ) type var;\n"); //$NON-NLS-1$
writer.write("decl1(int, x, y, z)\n"); //$NON-NLS-1$
writer.write("#define decl2(type, args...) type args;"); //$NON-NLS-1$
writer.write("decl2(int, a, b, c, x, y, z)\n"); //$NON-NLS-1$
writer.write("#define decl3(type, args...) \\\n type args;"); //$NON-NLS-1$
writer.write("decl3(int, a, b, c, x, y)\n"); //$NON-NLS-1$
writer.write("#define decl4(type, args... \\\n ) type args;"); //$NON-NLS-1$
writer.write("decl4(int, a, b, z)\n"); //$NON-NLS-1$
writer.write("#define decl5(type, ...) type __VA_ARGS__;"); //$NON-NLS-1$
writer.write("decl5(int, z)\n"); //$NON-NLS-1$
writer.write("#define decl6(type, ... \\\n) type __VA_ARGS__;"); //$NON-NLS-1$
writer.write("decl6(int, a, b, c, x)\n"); //$NON-NLS-1$
writer.write("#define foo(a) a __VA_ARGS__;\n"); //$NON-NLS-1$ C99: 6.10.3.5 this should produce an IProblem
writer.write("#define foo2(a) a #__VA_ARGS__;\n"); //$NON-NLS-1$ C99: 6.10.3.5 this should produce an IProblem
Callback callback = new Callback( ParserMode.COMPLETE_PARSE );
initializeScanner( writer.toString(), ParserMode.COMPLETE_PARSE, callback );
fullyTokenize();
Iterator probs = callback.problems.iterator();
assertTrue( probs.hasNext() );
assertTrue( ((IProblem)probs.next()).getID() == IProblem.PREPROCESSOR_INVALID_VA_ARGS );
assertTrue( probs.hasNext() );
assertTrue( ((IProblem)probs.next()).getID() == IProblem.PREPROCESSOR_MACRO_PASTING_ERROR );
assertFalse( probs.hasNext() );
}
public void testBug39688B() throws Exception { // test C99
Writer writer = new StringWriter();
writer.write("#define debug(...) fprintf(stderr, __VA_ARGS__)\n"); //$NON-NLS-1$
writer.write("#define showlist(...) puts(#__VA_ARGS__)\n"); //$NON-NLS-1$
writer.write("#define report(test, ...) ((test)?puts(#test):\\\n printf(__VA_ARGS__))\n"); //$NON-NLS-1$
writer.write("int main() {\n"); //$NON-NLS-1$
writer.write("debug(\"Flag\");\n"); //$NON-NLS-1$
writer.write("debug(\"X = %d\\n\", x);\n"); //$NON-NLS-1$
writer.write("showlist(The first, second, and third items.);\n"); //$NON-NLS-1$
writer.write("report(x>y, \"x is %d but y is %d\", x, y);\n"); //$NON-NLS-1$
writer.write("return 0; }\n"); //$NON-NLS-1$
Callback callback = new Callback( ParserMode.COMPLETE_PARSE );
initializeScanner( writer.toString(), ParserMode.COMPLETE_PARSE, callback );
fullyTokenize();
Iterator probs = callback.problems.iterator();
assertFalse( probs.hasNext() );
Map defs = scanner.getDefinitions();
assertTrue(defs.containsKey("debug")); //$NON-NLS-1$
assertTrue(defs.containsKey("showlist")); //$NON-NLS-1$
assertTrue(defs.containsKey("report")); //$NON-NLS-1$
FunctionStyleMacro debug = (FunctionStyleMacro)defs.get("debug"); //$NON-NLS-1$
assertTrue(new String(debug.arglist[0]).equals("__VA_ARGS__")); //$NON-NLS-1$
assertTrue(debug.hasVarArgs());
assertFalse(debug.hasGCCVarArgs());
assertTrue(new String(debug.expansion).equals("fprintf(stderr, __VA_ARGS__)") ); //$NON-NLS-1$
FunctionStyleMacro showlist = (FunctionStyleMacro)defs.get("showlist"); //$NON-NLS-1$
assertTrue(new String(showlist.arglist[0]).equals("__VA_ARGS__")); //$NON-NLS-1$
assertTrue(showlist.hasVarArgs());
assertFalse(showlist.hasGCCVarArgs());
assertTrue(new String(showlist.expansion).equals("puts(#__VA_ARGS__)")); //$NON-NLS-1$
FunctionStyleMacro report = (FunctionStyleMacro)defs.get("report"); //$NON-NLS-1$
assertTrue(new String(report.arglist[0]).equals("test")); //$NON-NLS-1$
assertTrue(new String(report.arglist[1]).equals("__VA_ARGS__")); //$NON-NLS-1$
assertTrue(report.hasVarArgs());
assertFalse(report.hasGCCVarArgs());
assertTrue(new String(report.expansion).equals("((test)?puts(#test): printf(__VA_ARGS__))")); //$NON-NLS-1$
validate39688Common(writer, callback);
}
public void testBug39688C() throws Exception { // test GCC
Writer writer = new StringWriter();
writer.write("#define debug(vars...) fprintf(stderr, vars)\n"); //$NON-NLS-1$
writer.write("#define showlist(vars...) puts(#vars)\n"); //$NON-NLS-1$
writer.write("#define report(test, vars...) ((test)?puts(#test):\\\n printf(vars))\n"); //$NON-NLS-1$
writer.write("int main() {\n"); //$NON-NLS-1$
writer.write("debug(\"Flag\");\n"); //$NON-NLS-1$
writer.write("debug(\"X = %d\\n\", x);\n"); //$NON-NLS-1$
writer.write("showlist(The first, second, and third items.);\n"); //$NON-NLS-1$
writer.write("report(x>y, \"x is %d but y is %d\", x, y);\n"); //$NON-NLS-1$
writer.write("return 0; }\n"); //$NON-NLS-1$
Callback callback = new Callback( ParserMode.COMPLETE_PARSE );
initializeScanner( writer.toString(), ParserMode.COMPLETE_PARSE, callback );
fullyTokenize();
Iterator probs = callback.problems.iterator();
assertFalse( probs.hasNext() );
Map defs = scanner.getDefinitions();
assertTrue(defs.containsKey("debug")); //$NON-NLS-1$
assertTrue(defs.containsKey("showlist")); //$NON-NLS-1$
assertTrue(defs.containsKey("report")); //$NON-NLS-1$
FunctionStyleMacro debug = (FunctionStyleMacro)defs.get("debug"); //$NON-NLS-1$
assertTrue(new String(debug.arglist[0]).equals("vars")); //$NON-NLS-1$
assertFalse(debug.hasVarArgs());
assertTrue(debug.hasGCCVarArgs());
assertTrue(new String(debug.expansion).equals("fprintf(stderr, vars)") ); //$NON-NLS-1$
FunctionStyleMacro showlist = (FunctionStyleMacro)defs.get("showlist"); //$NON-NLS-1$
assertTrue(new String(showlist.arglist[0]).equals("vars")); //$NON-NLS-1$
assertFalse(showlist.hasVarArgs());
assertTrue(showlist.hasGCCVarArgs());
assertTrue(new String(showlist.expansion).equals("puts(#vars)")); //$NON-NLS-1$
FunctionStyleMacro report = (FunctionStyleMacro)defs.get("report"); //$NON-NLS-1$
assertTrue(new String(report.arglist[0]).equals("test")); //$NON-NLS-1$
assertTrue(new String(report.arglist[1]).equals("vars")); //$NON-NLS-1$
assertFalse(report.hasVarArgs());
assertTrue(report.hasGCCVarArgs());
assertTrue(new String(report.expansion).equals("((test)?puts(#test): printf(vars))")); //$NON-NLS-1$
validate39688Common(writer, callback);
}
private void validate39688Common(Writer writer, Callback callback) throws Exception {
initializeScanner( writer.toString(), ParserMode.COMPLETE_PARSE, callback );
validateToken(IToken.t_int);
validateIdentifier("main"); //$NON-NLS-1$
validateToken(IToken.tLPAREN);
validateToken(IToken.tRPAREN);
validateToken(IToken.tLBRACE);
validateIdentifier("fprintf"); //$NON-NLS-1$
validateToken(IToken.tLPAREN);
validateIdentifier("stderr"); //$NON-NLS-1$
validateToken(IToken.tCOMMA);
validateString("Flag"); //$NON-NLS-1$
validateToken(IToken.tRPAREN);
validateToken(IToken.tSEMI);
validateIdentifier("fprintf"); //$NON-NLS-1$
validateToken(IToken.tLPAREN);
validateIdentifier("stderr"); //$NON-NLS-1$
validateToken(IToken.tCOMMA);
validateString("X = %d\\n"); //$NON-NLS-1$
validateToken(IToken.tCOMMA);
validateIdentifier("x"); //$NON-NLS-1$
validateToken(IToken.tRPAREN);
validateToken(IToken.tSEMI);
validateIdentifier("puts"); //$NON-NLS-1$
validateToken(IToken.tLPAREN);
validateString("The first, second, and third items."); //$NON-NLS-1$
validateToken(IToken.tRPAREN);
validateToken(IToken.tSEMI);
validateToken(IToken.tLPAREN);
validateToken(IToken.tLPAREN);
validateIdentifier("x"); //$NON-NLS-1$
validateToken(IToken.tGT);
validateIdentifier("y"); //$NON-NLS-1$
validateToken(IToken.tRPAREN);
validateToken(IToken.tQUESTION);
validateIdentifier("puts"); //$NON-NLS-1$
validateToken(IToken.tLPAREN);
validateString("x>y"); //$NON-NLS-1$
validateToken(IToken.tRPAREN);
validateToken(IToken.tCOLON);
validateIdentifier("printf"); //$NON-NLS-1$
validateToken(IToken.tLPAREN);
validateString("x is %d but y is %d"); //$NON-NLS-1$
validateToken(IToken.tCOMMA);
validateIdentifier("x"); //$NON-NLS-1$
validateToken(IToken.tCOMMA);
validateIdentifier("y"); //$NON-NLS-1$
validateToken(IToken.tRPAREN);
validateToken(IToken.tRPAREN);
validateToken(IToken.tSEMI);
validateToken(IToken.t_return);
validateInteger("0"); //$NON-NLS-1$
validateToken(IToken.tSEMI);
validateToken(IToken.tRBRACE);
validateEOF();
}
} }

View file

@ -644,7 +644,7 @@ public class SourceIndexerRequestor implements ISourceElementRequestor, IIndexCo
tempMarker = markers[i]; tempMarker = markers[i];
tempInt = (Integer) tempMarker.getAttribute(IMarker.LINE_NUMBER); tempInt = (Integer) tempMarker.getAttribute(IMarker.LINE_NUMBER);
tempMsgString = (String) tempMarker.getAttribute(IMarker.MESSAGE); tempMsgString = (String) tempMarker.getAttribute(IMarker.MESSAGE);
if (tempInt.intValue()==problem.getSourceLineNumber() && if (tempInt != null && tempInt.intValue()==problem.getSourceLineNumber() &&
tempMsgString.equalsIgnoreCase( INDEXER_MARKER_PREFIX + problem.getMessage())) tempMsgString.equalsIgnoreCase( INDEXER_MARKER_PREFIX + problem.getMessage()))
{ {
newProblem = false; newProblem = false;

View file

@ -402,6 +402,18 @@ public interface IProblem
*/ */
public final static int PREPROCESSOR_CIRCULAR_INCLUSION = PREPROCESSOR_RELATED | 0x00B; public final static int PREPROCESSOR_CIRCULAR_INCLUSION = PREPROCESSOR_RELATED | 0x00B;
/**
* macro argument "..." encountered without the required ')' i.e. must be last argument if used
* Required attributes: none
*/
public final static int PREPROCESSOR_MISSING_RPAREN_PARMLIST = PREPROCESSOR_RELATED | 0x00C;
/**
* __VA_ARGS__ encountered in macro definition without the required '...' parameter
* Required attributes: none
*/
public final static int PREPROCESSOR_INVALID_VA_ARGS = PREPROCESSOR_RELATED | 0x00D;
/* /*
* Parser Syntactic Problems * Parser Syntactic Problems
*/ */

View file

@ -33,6 +33,8 @@ ScannerProblemFactory.error.preproc.macroUsage=Macro usage error for macro : {0}
ScannerProblemFactory.error.preproc.circularInclusion=Circular inclusion for file : {0} ScannerProblemFactory.error.preproc.circularInclusion=Circular inclusion for file : {0}
ScannerProblemFactory.error.preproc.invalidDirective=Invalid preprocessor directive : {0} ScannerProblemFactory.error.preproc.invalidDirective=Invalid preprocessor directive : {0}
ScannerProblemFactory.error.preproc.macroPasting=Invalid use of macro pasting in macro : {0} ScannerProblemFactory.error.preproc.macroPasting=Invalid use of macro pasting in macro : {0}
ScannerProblemFactory.error.preproc.missingRParen=missing ) in macro parameter list
ScannerProblemFactory.error.preproc.invalidVaArgs=__VA_ARGS__ can only appear in the expansion of a C99 variadic macro
ScannerProblemFactory.error.scanner.invalidEscapeChar=Invalid escape character encountered ScannerProblemFactory.error.scanner.invalidEscapeChar=Invalid escape character encountered
ScannerProblemFactory.error.scanner.unboundedString=Unbounded string encountered ScannerProblemFactory.error.scanner.unboundedString=Unbounded string encountered

View file

@ -181,6 +181,12 @@ public class Problem implements IProblem {
errorMessages.put( errorMessages.put(
new Integer(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR), new Integer(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR),
ParserMessages.getString("ScannerProblemFactory.error.preproc.macroPasting")); //$NON-NLS-1$ ParserMessages.getString("ScannerProblemFactory.error.preproc.macroPasting")); //$NON-NLS-1$
errorMessages.put(
new Integer(IProblem.PREPROCESSOR_MISSING_RPAREN_PARMLIST),
ParserMessages.getString("ScannerProblemFactory.error.preproc.missingRParen")); //$NON-NLS-1$
errorMessages.put(
new Integer(IProblem.PREPROCESSOR_INVALID_VA_ARGS),
ParserMessages.getString("ScannerProblemFactory.error.preproc.invalidVaArgs")); //$NON-NLS-1$
errorMessages.put( errorMessages.put(
new Integer(IProblem.SCANNER_INVALID_ESCAPECHAR), new Integer(IProblem.SCANNER_INVALID_ESCAPECHAR),
ParserMessages.getString("ScannerProblemFactory.error.scanner.invalidEscapeChar")); //$NON-NLS-1$ ParserMessages.getString("ScannerProblemFactory.error.scanner.invalidEscapeChar")); //$NON-NLS-1$

View file

@ -178,7 +178,7 @@ public class TemplateFactory extends ExtensibleSymbol implements ITemplateFactor
private void basicTemplateDeclaration( ISymbol symbol ) throws ParserSymbolTableException{ private void basicTemplateDeclaration( ISymbol symbol ) throws ParserSymbolTableException{
ITemplateSymbol template = (ITemplateSymbol)templates.get( 0 ); ITemplateSymbol template = (ITemplateSymbol)templates.get( 0 );
if( template == null ) return;
if( template.getParameterList().size() == 0 ){ if( template.getParameterList().size() == 0 ){
//explicit specialization, deduce some arguments and use addTemplateId //explicit specialization, deduce some arguments and use addTemplateId
ISymbol previous = findPreviousSymbol( symbol, new ArrayList() ); ISymbol previous = findPreviousSymbol( symbol, new ArrayList() );
@ -380,7 +380,7 @@ public class TemplateFactory extends ExtensibleSymbol implements ITemplateFactor
int size = templates.size(); int size = templates.size();
for( int i = size - 1; i >= 0; i-- ){ for( int i = size - 1; i >= 0; i-- ){
ITemplateSymbol template = (ITemplateSymbol) templates.get(i); ITemplateSymbol template = (ITemplateSymbol) templates.get(i);
if( template == null )continue;
ISymbol look = template.lookupMemberForDefinition( name ); ISymbol look = template.lookupMemberForDefinition( name );
if( look != null && look.isType( type ) ){ if( look != null && look.isType( type ) ){
return look; return look;
@ -397,12 +397,14 @@ public class TemplateFactory extends ExtensibleSymbol implements ITemplateFactor
int size = templates.size(); int size = templates.size();
for( int i = size - 1; i >= 0; i-- ){ for( int i = size - 1; i >= 0; i-- ){
ITemplateSymbol template = (ITemplateSymbol) templates.get(i); ITemplateSymbol template = (ITemplateSymbol) templates.get(i);
if( template != null )
{
ISymbol look = template.lookupMemberForDefinition( name ); ISymbol look = template.lookupMemberForDefinition( name );
if( look != null ){ if( look != null ){
return look; return look;
} }
} }
}
return getContainingSymbol().lookup( name ); return getContainingSymbol().lookup( name );
} }

View file

@ -11,17 +11,61 @@
package org.eclipse.cdt.internal.core.parser.scanner2; package org.eclipse.cdt.internal.core.parser.scanner2;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap; import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
/** /**
* @author Doug Schaefer * @author Doug Schaefer
*/ */
public class FunctionStyleMacro extends ObjectStyleMacro { public class FunctionStyleMacro extends ObjectStyleMacro {
private static final char[] VA_ARGS_CHARARRAY = "__VA_ARGS__".toCharArray(); //$NON-NLS-1$
private static final char[] ELLIPSIS_CHARARRAY = "...".toString().toCharArray(); //$NON-NLS-1$
public char[][] arglist; public char[][] arglist;
private char[] sig = null; private char[] sig = null;
private boolean hasVarArgs = false;
private boolean hasGCCVarArgs = false;
private int varArgsPosition = -1;
public FunctionStyleMacro(char[] name, char[] expansion, char[][] arglist) { public FunctionStyleMacro(char[] name, char[] expansion, char[][] arglist) {
super(name, expansion); super(name, expansion);
this.arglist = arglist; this.arglist = arglist;
// determine if there's an argument with "..."
if (arglist != null && arglist[0]!= null && arglist.length > 0) {
int last = -1;
// if the last element in the list is null then binary search for the last non-null element
if (arglist[arglist.length-1] == null) {
int largest = arglist.length - 1;
int smallest = 0;
for (int j=arglist.length/2; last == -1; ) {
if (arglist[j] == null) {
largest = j;
j=smallest + (largest-smallest)/2;
} else {
smallest = j;
j=smallest + (largest - smallest)/2;
if ((j+1 == arglist.length && arglist[j] != null) || (arglist[j] != null && arglist[j+1] == null))
last = j;
}
}
} else
last = arglist.length-1;
if (arglist[last] != null && CharArrayUtils.equals(arglist[last], ELLIPSIS_CHARARRAY)) {
this.hasVarArgs = true;
varArgsPosition = last;
// change the arg to __VA_ARGS__ so this will be replaced properly later on...
arglist[last] = VA_ARGS_CHARARRAY;
} else if (arglist[last] != null && CharArrayUtils.equals(arglist[last], arglist[last].length - ELLIPSIS_CHARARRAY.length, ELLIPSIS_CHARARRAY.length, ELLIPSIS_CHARARRAY)) { // if the last 3 are '...'
this.hasGCCVarArgs = true;
varArgsPosition = last;
// change the arg to "argname" instead of "argname..." so argname will be replaced properly later on...
char[] swap = new char[arglist[last].length - ELLIPSIS_CHARARRAY.length];
System.arraycopy(arglist[last], 0, swap, 0, swap.length);
arglist[last] = swap;
}
}
} }
public char[] getSignature(){ public char[] getSignature(){
@ -53,4 +97,16 @@ public class FunctionStyleMacro extends ObjectStyleMacro {
= new CharArrayObjectMap(FunctionStyleMacro.this.arglist.length); = new CharArrayObjectMap(FunctionStyleMacro.this.arglist.length);
} }
public boolean hasVarArgs() {
return hasVarArgs;
}
public boolean hasGCCVarArgs() {
return hasGCCVarArgs;
}
public int getVarArgsPosition() {
return varArgsPosition;
}
} }

View file

@ -57,6 +57,8 @@ import org.eclipse.cdt.internal.core.parser.token.SimpleToken;
*/ */
public class Scanner2 implements IScanner, IScannerData { public class Scanner2 implements IScanner, IScannerData {
private static final char[] ELLIPSIS_CHARARRAY = "...".toString().toCharArray(); //$NON-NLS-1$
private static final char[] VA_ARGS_CHARARRAY = "__VA_ARGS__".toCharArray(); //$NON-NLS-1$
/** /**
* @author jcamelon * @author jcamelon
* *
@ -504,7 +506,7 @@ public class Scanner2 implements IScanner, IScannerData {
if (pos + 1 < limit && buffer[pos + 1] == '"') if (pos + 1 < limit && buffer[pos + 1] == '"')
return scanString(); return scanString();
if (pos + 1 < limit && buffer[pos + 1] == '\'') if (pos + 1 < limit && buffer[pos + 1] == '\'')
return scanCharLiteral(true); return scanCharLiteral();
IToken t = scanIdentifier(); IToken t = scanIdentifier();
if (t instanceof MacroExpansionToken) if (t instanceof MacroExpansionToken)
@ -516,7 +518,7 @@ public class Scanner2 implements IScanner, IScannerData {
return scanString(); return scanString();
case '\'': case '\'':
return scanCharLiteral(false); return scanCharLiteral();
case 'a': case 'a':
case 'b': case 'b':
@ -1064,7 +1066,7 @@ public class Scanner2 implements IScanner, IScannerData {
return newToken(tokenType, result); return newToken(tokenType, result);
} }
private IToken scanCharLiteral(boolean b) { private IToken scanCharLiteral() {
char[] buffer = bufferStack[bufferStackPos]; char[] buffer = bufferStack[bufferStackPos];
int start = bufferPos[bufferStackPos]; int start = bufferPos[bufferStackPos];
int limit = bufferLimit[bufferStackPos]; int limit = bufferLimit[bufferStackPos];
@ -1735,10 +1737,18 @@ public class Scanner2 implements IScanner, IScannerData {
skipOverWhiteSpace(); skipOverWhiteSpace();
int textstart = bufferPos[bufferStackPos] + 1; int textstart = bufferPos[bufferStackPos] + 1;
int textend = textstart - 1; int textend = textstart - 1;
int varArgDefinitionInd = -1;
boolean encounteredMultilineComment = false; boolean encounteredMultilineComment = false;
boolean usesVarArgInDefinition = false;
while (bufferPos[bufferStackPos] + 1 < limit while (bufferPos[bufferStackPos] + 1 < limit
&& buffer[bufferPos[bufferStackPos] + 1] != '\n') { && buffer[bufferPos[bufferStackPos] + 1] != '\n') {
if (CharArrayUtils.equals( buffer, bufferPos[bufferStackPos] + 1, VA_ARGS_CHARARRAY.length, VA_ARGS_CHARARRAY )) {
usesVarArgInDefinition = true; // __VA_ARGS__ is in definition, used to check C99 6.10.3-5
varArgDefinitionInd = bufferPos[bufferStackPos] + 1;
}
//16.3.2-1 Each # preprocessing token in the replacement list for a function-like-macro shall //16.3.2-1 Each # preprocessing token in the replacement list for a function-like-macro shall
//be followed by a parameter as the next preprocessing token //be followed by a parameter as the next preprocessing token
if( arglist != null && !skipOverNonWhiteSpace( true ) ){ if( arglist != null && !skipOverNonWhiteSpace( true ) ){
@ -1754,7 +1764,17 @@ public class Scanner2 implements IScanner, IScannerData {
{ {
if( bufferPos[bufferStackPos] + arglist[i].length - 1 < limit ) if( bufferPos[bufferStackPos] + arglist[i].length - 1 < limit )
{ {
if( CharArrayUtils.equals( buffer, bufferPos[bufferStackPos], arglist[i].length, arglist[i] ) ) if (arglist[i].length > 3 && arglist[i][arglist[i].length - 3] == '.' && arglist[i][arglist[i].length - 2] == '.' && arglist[i][arglist[i].length - 3] == '.') {
char[] varArgName = new char[arglist[i].length - 3];
System.arraycopy(arglist[i], 0, varArgName, 0, arglist[i].length - 3);
if (CharArrayUtils.equals( buffer, bufferPos[bufferStackPos], varArgName.length, varArgName)) {
isArg = true;
//advance us to the end of the arg
bufferPos[bufferStackPos] += arglist[i].length - 4;
break;
}
} else if ( CharArrayUtils.equals( buffer, bufferPos[bufferStackPos], arglist[i].length, arglist[i] )
|| (CharArrayUtils.equals(arglist[i], ELLIPSIS_CHARARRAY) && CharArrayUtils.equals( buffer, bufferPos[bufferStackPos], VA_ARGS_CHARARRAY.length, VA_ARGS_CHARARRAY )))
{ {
isArg = true; isArg = true;
//advance us to the end of the arg //advance us to the end of the arg
@ -1791,6 +1811,9 @@ public class Scanner2 implements IScanner, IScannerData {
? new ObjectStyleMacro(name, text) ? new ObjectStyleMacro(name, text)
: new FunctionStyleMacro(name, text, arglist) ); : new FunctionStyleMacro(name, text, arglist) );
if (usesVarArgInDefinition && definitions.get(name) instanceof FunctionStyleMacro && !((FunctionStyleMacro)definitions.get(name)).hasVarArgs())
handleProblem(IProblem.PREPROCESSOR_INVALID_VA_ARGS, varArgDefinitionInd, null);
callbackManager.pushCallback( getASTFactory().createMacro( name, startingOffset, startingLineNumber, idstart, idstart + idlen, nameLine, textstart + textlen, endingLine, getCurrentFilename() ) ); callbackManager.pushCallback( getASTFactory().createMacro( name, startingOffset, startingLineNumber, idstart, idstart + idlen, nameLine, textstart + textlen, endingLine, getCurrentFilename() ) );
} }
@ -1805,31 +1828,29 @@ public class Scanner2 implements IScanner, IScannerData {
char[][] arglist = new char[4][]; char[][] arglist = new char[4][];
int currarg = -1; int currarg = -1;
while (bufferPos[bufferStackPos] < limit) { while (bufferPos[bufferStackPos] < limit) {
int pos = bufferPos[bufferStackPos];
skipOverWhiteSpace(); skipOverWhiteSpace();
if (++bufferPos[bufferStackPos] >= limit) if (++bufferPos[bufferStackPos] >= limit)
return null; return null;
c = buffer[bufferPos[bufferStackPos]]; c = buffer[bufferPos[bufferStackPos]];
int argstart = bufferPos[bufferStackPos];
if (c == ')') { if (c == ')') {
break; break;
} else if (c == ',') { } else if (c == ',') {
continue; continue;
} else if (c == '.' } else if (c == '.'
&& pos + 1 < limit && buffer[pos + 1] == '.' && bufferPos[bufferStackPos] + 1 < limit && buffer[bufferPos[bufferStackPos] + 1] == '.'
&& pos + 2 < limit && buffer[pos + 2] == '.') { && bufferPos[bufferStackPos] + 2 < limit && buffer[bufferPos[bufferStackPos] + 2] == '.') {
// varargs bufferPos[bufferStackPos]--; // move back and let skipOverIdentifier handle the ellipsis
// TODO - something better
bufferPos[bufferStackPos] += 2;
arglist[++currarg] = "...".toCharArray(); //$NON-NLS-1$
continue;
} else if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Character.isUnicodeIdentifierPart(c))) { } else if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Character.isUnicodeIdentifierPart(c))) {
if( reportProblems ) if( reportProblems ) {
handleProblem( IProblem.PREPROCESSOR_INVALID_MACRO_DEFN, idstart, name ); handleProblem( IProblem.PREPROCESSOR_INVALID_MACRO_DEFN, idstart, name );
// yuck // yuck
skipToNewLine(); skipToNewLine();
return null; return null;
} }
int argstart = bufferPos[bufferStackPos]; }
skipOverIdentifier(); skipOverIdentifier();
if (++currarg == arglist.length) { if (++currarg == arglist.length) {
char[][] oldarglist = arglist; char[][] oldarglist = arglist;
@ -1858,6 +1879,8 @@ public class Scanner2 implements IScanner, IScannerData {
{ {
if( text[i] == '\\' && i+ 1 < text.length && text[i+1] == '\n' ) if( text[i] == '\\' && i+ 1 < text.length && text[i+1] == '\n' )
++i; ++i;
else if( text[i] == '\\' && i + 1 < text.length && text[i+1] == '\r' && i + 2 < text.length && text[i+2] == '\n' )
i+=2;
else else
result[ counter++ ] = text[i]; result[ counter++ ] = text[i];
} }
@ -2223,6 +2246,14 @@ public class Scanner2 implements IScanner, IScannerData {
--bufferPos[bufferStackPos]; --bufferPos[bufferStackPos];
return true; return true;
} }
if( pos + 1 < limit && buffer[ pos + 1 ] == '\r')
{
if( pos + 2 < limit && buffer[ pos + 2] == '\n' )
{
bufferPos[bufferStackPos] +=2;
continue;
}
}
break; break;
case '"': case '"':
boolean escaped = false; boolean escaped = false;
@ -2355,13 +2386,56 @@ public class Scanner2 implements IScanner, IScannerData {
while (++bufferPos[bufferStackPos] < limit) { while (++bufferPos[bufferStackPos] < limit) {
char c = buffer[bufferPos[bufferStackPos]]; char c = buffer[bufferPos[bufferStackPos]];
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') if (c == '.' && bufferPos[bufferStackPos] + 1 < limit && buffer[bufferPos[bufferStackPos] + 1] == '.'
&& bufferPos[bufferStackPos] + 2 < limit && buffer[bufferPos[bufferStackPos] + 2] == '.') {
// encountered "..." make sure it's the last argument, if not raise IProblem
bufferPos[bufferStackPos] += 2;
int end = bufferPos[bufferStackPos];
while(++bufferPos[bufferStackPos] < limit) {
char c2 = buffer[bufferPos[bufferStackPos]];
if (c2 == ')') { // good
bufferPos[bufferStackPos] = end; // point at the end of ... to get the argument
return;
}
switch (c2) {
case ' ':
case '\t':
case '\r':
continue;
case '\\':
if (bufferPos[bufferStackPos] + 1 < limit && buffer[bufferPos[bufferStackPos] + 1] == '\n') {
// \n is a whitespace
++bufferPos[bufferStackPos];
continue;
}
if( bufferPos[bufferStackPos] + 1 < limit && buffer[ bufferPos[bufferStackPos] + 1 ] == '\r')
{
if( bufferPos[bufferStackPos] + 2 < limit && buffer[ bufferPos[bufferStackPos] + 2] == '\n' )
{
bufferPos[bufferStackPos] +=2;
continue;
}
}
break;
default:
// bad
handleProblem( IProblem.PREPROCESSOR_MISSING_RPAREN_PARMLIST, bufferPos[bufferStackPos], String.valueOf(c2).toCharArray() );
return;
}
}
// "..." was the last macro argument
break;
} else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|| c == '_' || (c >= '0' && c <= '9') || Character.isUnicodeIdentifierPart(c)) { || c == '_' || (c >= '0' && c <= '9') || Character.isUnicodeIdentifierPart(c)) {
continue; continue;
} }
break; break; // found the end of the argument
} }
--bufferPos[bufferStackPos]; --bufferPos[bufferStackPos];
} }
@ -2466,14 +2540,26 @@ public class Scanner2 implements IScanner, IScannerData {
continue; continue;
} }
if (++currarg >= arglist.length || arglist[currarg] == null){ if ((++currarg >= arglist.length || arglist[currarg] == null) && !macro.hasVarArgs() && !macro.hasGCCVarArgs()) {
// too many args // too many args and no variable argument
handleProblem( IProblem.PREPROCESSOR_MACRO_USAGE_ERROR, bufferPos[bufferStackPos], macro.name ); handleProblem( IProblem.PREPROCESSOR_MACRO_USAGE_ERROR, bufferPos[bufferStackPos], macro.name );
break; break;
} }
int argstart = bufferPos[bufferStackPos]; int argstart = bufferPos[bufferStackPos];
int argend = skipOverMacroArg();
int argend = -1;
if ((macro.hasGCCVarArgs() || macro.hasVarArgs()) && currarg == macro.getVarArgsPosition()) {
// there are varargs and the other parms have been accounted for, the rest will replace __VA_ARGS__ or name where "name..." is the parm
while (++bufferPos[bufferStackPos] < limit) {
if (buffer[bufferPos[bufferStackPos]] == ')') {
--bufferPos[bufferStackPos];
break;
}
}
argend = bufferPos[bufferStackPos];
} else
argend = skipOverMacroArg();
char[] arg = emptyCharArray; char[] arg = emptyCharArray;
int arglen = argend - argstart + 1; int arglen = argend - argstart + 1;