Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[cdt-patch] [FIXED] 39688 [Scanner] Macros with variable number of arguments are not supported (C99) (GCC)


This is a patch for 39688 that has not yet been tested against a large project that uses macros with variable number of arguments.  The test cases were pulled from C99 and work well.  Let me know how this holds up against something like Etherial.

[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

Devin Steffler
IBM's Eclipse CDT
Ottawa (Palladium), Ontario, Canada


Index: parser/org/eclipse/cdt/core/parser/tests/scanner2/Scanner2Test.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt-core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/scanner2/Scanner2Test.java,v
retrieving revision 1.33
diff -u -r1.33 Scanner2Test.java
--- parser/org/eclipse/cdt/core/parser/tests/scanner2/Scanner2Test.java	13 Oct 2004 20:03:27 -0000	1.33
+++ parser/org/eclipse/cdt/core/parser/tests/scanner2/Scanner2Test.java	18 Oct 2004 13:59:04 -0000
@@ -16,6 +16,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import org.eclipse.cdt.core.parser.IProblem;
 import org.eclipse.cdt.core.parser.ISourceElementRequestor;
@@ -26,6 +27,7 @@
 import org.eclipse.cdt.core.parser.ParserMode;
 import org.eclipse.cdt.core.parser.ast.IASTInclusion;
 import org.eclipse.cdt.internal.core.parser.QuickParseCallback;
+import org.eclipse.cdt.internal.core.parser.scanner2.FunctionStyleMacro;
 
 /**
  * @author jcamelon
@@ -1972,5 +1974,181 @@
     	assertTrue(((IProblem)probs.next()).getID() ==  IProblem.SCANNER_MISSING_R_PAREN );
     	assertTrue(((IProblem)probs.next()).getID() ==  IProblem.SCANNER_ASSIGNMENT_NOT_ALLOWED );
     	assertTrue(((IProblem)probs.next()).getID() ==  IProblem.SCANNER_BAD_OCTAL_FORMAT );
+    }
+    
+    public void testBug39688A() throws Exception { // test valid IProblems
+    	Writer writer = new StringWriter();
+    	writer.write("#define decl1(type, ...    \\\n  )   type var;\n");
+    	writer.write("decl1(int, x, y, z)\n");
+    	writer.write("#define decl2(type, args...) type args;");
+    	writer.write("decl2(int, a, b, c, x, y, z)\n");
+    	writer.write("#define decl3(type, args...) \\\n   type args;");
+    	writer.write("decl3(int, a, b, c, x, y)\n");
+    	writer.write("#define decl4(type, args... \\\n   ) type args;");
+    	writer.write("decl4(int, a, b, z)\n");
+    	writer.write("#define decl5(type, ...) type __VA_ARGS__;");
+    	writer.write("decl5(int, z)\n");
+    	writer.write("#define decl6(type, ...    \\\n) type __VA_ARGS__;");
+    	writer.write("decl6(int, a, b, c, x)\n");    	
+    	writer.write("#define foo(a) a __VA_ARGS__;\n"); // C99: 6.10.3.5 this should produce an IProblem
+    	writer.write("#define foo2(a) a #__VA_ARGS__;\n"); // 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");
+    	writer.write("#define showlist(...) puts(#__VA_ARGS__)\n");
+		writer.write("#define report(test, ...) ((test)?puts(#test):\\\n   printf(__VA_ARGS__))\n");
+		writer.write("int main() {\n");
+		writer.write("debug(\"Flag\");\n");
+		writer.write("debug(\"X = %d\\n\", x);\n");
+		writer.write("showlist(The first, second, and third items.);\n");
+		writer.write("report(x>y, \"x is %d but y is %d\", x, y);\n");
+		writer.write("return 0; }\n");
+		
+    	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"));
+    	assertTrue(defs.containsKey("showlist"));
+    	assertTrue(defs.containsKey("report"));
+    	FunctionStyleMacro debug = (FunctionStyleMacro)defs.get("debug");
+    	assertTrue(new String(debug.arglist[0]).equals("__VA_ARGS__"));
+    	assertTrue(debug.hasVarArgs());
+    	assertFalse(debug.hasGCCVarArgs());
+    	assertTrue(new String(debug.expansion).equals("fprintf(stderr, __VA_ARGS__)") );
+    	FunctionStyleMacro showlist = (FunctionStyleMacro)defs.get("showlist");
+    	assertTrue(new String(showlist.arglist[0]).equals("__VA_ARGS__"));
+    	assertTrue(showlist.hasVarArgs());
+    	assertFalse(showlist.hasGCCVarArgs());
+    	assertTrue(new String(showlist.expansion).equals("puts(#__VA_ARGS__)"));
+    	FunctionStyleMacro report = (FunctionStyleMacro)defs.get("report");
+    	assertTrue(new String(report.arglist[0]).equals("test"));
+    	assertTrue(new String(report.arglist[1]).equals("__VA_ARGS__"));
+    	assertTrue(report.hasVarArgs());
+    	assertFalse(report.hasGCCVarArgs());
+    	assertTrue(new String(report.expansion).equals("((test)?puts(#test):   printf(__VA_ARGS__))"));
+
+    	validate39688Common(writer, callback);
+    }
+    
+    public void testBug39688C() throws Exception { // test GCC
+    	Writer writer = new StringWriter();
+    	writer.write("#define debug(vars...) fprintf(stderr, vars)\n");
+    	writer.write("#define showlist(vars...) puts(#vars)\n");
+		writer.write("#define report(test, vars...) ((test)?puts(#test):\\\n   printf(vars))\n");
+		writer.write("int main() {\n");
+		writer.write("debug(\"Flag\");\n");
+		writer.write("debug(\"X = %d\\n\", x);\n");
+		writer.write("showlist(The first, second, and third items.);\n");
+		writer.write("report(x>y, \"x is %d but y is %d\", x, y);\n");
+		writer.write("return 0; }\n");
+		
+    	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"));
+    	assertTrue(defs.containsKey("showlist"));
+    	assertTrue(defs.containsKey("report"));
+    	FunctionStyleMacro debug = (FunctionStyleMacro)defs.get("debug");
+    	assertTrue(new String(debug.arglist[0]).equals("vars"));
+    	assertFalse(debug.hasVarArgs());
+    	assertTrue(debug.hasGCCVarArgs());
+    	assertTrue(new String(debug.expansion).equals("fprintf(stderr, vars)") );
+    	FunctionStyleMacro showlist = (FunctionStyleMacro)defs.get("showlist");
+    	assertTrue(new String(showlist.arglist[0]).equals("vars"));
+    	assertFalse(showlist.hasVarArgs());
+    	assertTrue(showlist.hasGCCVarArgs());
+    	assertTrue(new String(showlist.expansion).equals("puts(#vars)"));
+    	FunctionStyleMacro report = (FunctionStyleMacro)defs.get("report");
+    	assertTrue(new String(report.arglist[0]).equals("test"));
+    	assertTrue(new String(report.arglist[1]).equals("vars"));
+    	assertFalse(report.hasVarArgs());
+    	assertTrue(report.hasGCCVarArgs());
+    	assertTrue(new String(report.expansion).equals("((test)?puts(#test):   printf(vars))"));
+    	
+    	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");
+		validateToken(IToken.tLPAREN);
+		validateToken(IToken.tRPAREN);
+		validateToken(IToken.tLBRACE);
+		
+		validateIdentifier("fprintf");
+		validateToken(IToken.tLPAREN);
+		validateIdentifier("stderr");
+		validateToken(IToken.tCOMMA);
+		validateString("Flag");
+		validateToken(IToken.tRPAREN);
+		validateToken(IToken.tSEMI);
+		
+		validateIdentifier("fprintf");
+		validateToken(IToken.tLPAREN);
+		validateIdentifier("stderr");
+		validateToken(IToken.tCOMMA);
+		validateString("X = %d\\n");
+		validateToken(IToken.tCOMMA);
+		validateIdentifier("x");
+		validateToken(IToken.tRPAREN);
+		validateToken(IToken.tSEMI);
+		
+		validateIdentifier("puts");
+		validateToken(IToken.tLPAREN);
+		validateString("The first, second, and third items.");
+		validateToken(IToken.tRPAREN);
+		validateToken(IToken.tSEMI);
+		
+		validateToken(IToken.tLPAREN);
+		validateToken(IToken.tLPAREN);
+		validateIdentifier("x");
+		validateToken(IToken.tGT);
+		validateIdentifier("y");
+		validateToken(IToken.tRPAREN);
+		validateToken(IToken.tQUESTION);
+		validateIdentifier("puts");
+		validateToken(IToken.tLPAREN);
+		validateString("x>y");
+		validateToken(IToken.tRPAREN);
+		validateToken(IToken.tCOLON);
+		validateIdentifier("printf");
+		validateToken(IToken.tLPAREN);
+		validateString("x is %d but y is %d");
+		validateToken(IToken.tCOMMA);
+		validateIdentifier("x");
+		validateToken(IToken.tCOMMA);
+		validateIdentifier("y");
+		validateToken(IToken.tRPAREN);
+		validateToken(IToken.tRPAREN);
+		validateToken(IToken.tSEMI);
+		
+		validateToken(IToken.t_return);
+		validateInteger("0");
+		validateToken(IToken.tSEMI);
+		validateToken(IToken.tRBRACE);
+		
+		validateEOF();
     }
 }
Index: parser/org/eclipse/cdt/core/parser/IProblem.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt-core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/IProblem.java,v
retrieving revision 1.12
diff -u -r1.12 IProblem.java
--- parser/org/eclipse/cdt/core/parser/IProblem.java	13 Oct 2004 20:03:32 -0000	1.12
+++ parser/org/eclipse/cdt/core/parser/IProblem.java	18 Oct 2004 13:49:09 -0000
@@ -401,7 +401,19 @@
 	 * @see #A_PREPROC_INCLUDE_FILENAME
 	 */	
 	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
 	 */
Index: parser/org/eclipse/cdt/internal/core/parser/ParserMessages.properties
===================================================================
RCS file: /home/tools/org.eclipse.cdt-core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/ParserMessages.properties,v
retrieving revision 1.6
diff -u -r1.6 ParserMessages.properties
--- parser/org/eclipse/cdt/internal/core/parser/ParserMessages.properties	13 Oct 2004 20:03:32 -0000	1.6
+++ parser/org/eclipse/cdt/internal/core/parser/ParserMessages.properties	18 Oct 2004 13:49:10 -0000
@@ -33,6 +33,8 @@
 ScannerProblemFactory.error.preproc.circularInclusion=Circular inclusion for file : {0}
 ScannerProblemFactory.error.preproc.invalidDirective=Invalid preprocessor directive : {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.unboundedString=Unbounded string encountered 
Index: parser/org/eclipse/cdt/internal/core/parser/problem/Problem.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt-core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/problem/Problem.java,v
retrieving revision 1.7
diff -u -r1.7 Problem.java
--- parser/org/eclipse/cdt/internal/core/parser/problem/Problem.java	13 Oct 2004 20:03:32 -0000	1.7
+++ parser/org/eclipse/cdt/internal/core/parser/problem/Problem.java	18 Oct 2004 13:49:10 -0000
@@ -182,6 +182,12 @@
 			new Integer(IProblem.PREPROCESSOR_MACRO_PASTING_ERROR),
 			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(
 			new Integer(IProblem.SCANNER_INVALID_ESCAPECHAR),
 			ParserMessages.getString("ScannerProblemFactory.error.scanner.invalidEscapeChar")); //$NON-NLS-1$
 		errorMessages.put(
Index: parser/org/eclipse/cdt/internal/core/parser/scanner2/FunctionStyleMacro.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt-core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner2/FunctionStyleMacro.java,v
retrieving revision 1.4
diff -u -r1.4 FunctionStyleMacro.java
--- parser/org/eclipse/cdt/internal/core/parser/scanner2/FunctionStyleMacro.java	17 Sep 2004 14:10:27 -0000	1.4
+++ parser/org/eclipse/cdt/internal/core/parser/scanner2/FunctionStyleMacro.java	18 Oct 2004 13:49:10 -0000
@@ -19,9 +19,50 @@
 
 	public char[][] arglist;
 	private char[] sig = null;
+	private boolean hasVarArgs = false;
+	private boolean hasGCCVarArgs = false;
+	private int varArgsPosition = -1;
+	
 	public FunctionStyleMacro(char[] name, char[] expansion, char[][] arglist) {
 		super(name, expansion);
 		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 && arglist[last].length == 3 && arglist[last][0] == '.' && arglist[last][1] == '.' && arglist[last][2] == '.') {
+				this.hasVarArgs = true;
+				varArgsPosition = last;
+				// change the arg to __VA_ARGS__ so this will be replaced properly later on...
+				arglist[last] = "__VA_ARGS__".toCharArray();
+			} else if (arglist[last] != null && arglist[last].length > 3 && arglist[last][arglist[last].length - 3] == '.' && arglist[last][arglist[last].length - 2] == '.' && arglist[last][arglist[last].length - 1] == '.') { // 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 - 3];
+				System.arraycopy(arglist[last], 0, swap, 0, swap.length);
+				arglist[last] = swap;
+			}
+		}
 	}
 	
 	public char[] getSignature(){
@@ -52,5 +93,17 @@
 		public final CharArrayObjectMap definitions
 			= new CharArrayObjectMap(FunctionStyleMacro.this.arglist.length);
 		
+	}
+	
+	public boolean hasVarArgs() {
+		return hasVarArgs;
+	}
+	
+	public boolean hasGCCVarArgs() {
+		return hasGCCVarArgs;
+	}
+	
+	public int getVarArgsPosition() {
+		return varArgsPosition;
 	}
 }
Index: parser/org/eclipse/cdt/internal/core/parser/scanner2/Scanner2.java
===================================================================
RCS file: /home/tools/org.eclipse.cdt-core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner2/Scanner2.java,v
retrieving revision 1.79
diff -u -r1.79 Scanner2.java
--- parser/org/eclipse/cdt/internal/core/parser/scanner2/Scanner2.java	13 Oct 2004 20:03:32 -0000	1.79
+++ parser/org/eclipse/cdt/internal/core/parser/scanner2/Scanner2.java	18 Oct 2004 13:49:12 -0000
@@ -528,7 +528,7 @@
 					if (pos + 1 < limit && buffer[pos + 1] == '"')
 						return scanString();
 					if (pos + 1 < limit && buffer[pos + 1] == '\'')
-						return scanCharLiteral(true);
+						return scanCharLiteral();
 					
 					IToken t = scanIdentifier();
 					if (t instanceof MacroExpansionToken)
@@ -540,7 +540,7 @@
 					return scanString();
 					
 				case '\'':
-					return scanCharLiteral(false);
+					return scanCharLiteral();
 
 				case 'a':
 				case 'b':
@@ -1088,7 +1088,7 @@
 		return newToken(tokenType, result);
 	}
 
-	private IToken scanCharLiteral(boolean b) {
+	private IToken scanCharLiteral() {
 		char[] buffer = bufferStack[bufferStackPos];
 		int start = bufferPos[bufferStackPos];
 		int limit = bufferLimit[bufferStackPos];
@@ -1759,11 +1759,19 @@
 		skipOverWhiteSpace();
 		int textstart = bufferPos[bufferStackPos] + 1;
 		int textend = textstart - 1;
+		int varArgDefinitionInd = -1;
 		
 		boolean encounteredMultilineComment = false;
+	    boolean usesVarArgInDefinition = false;
 		while (bufferPos[bufferStackPos] + 1 < limit
 				&& buffer[bufferPos[bufferStackPos] + 1] != '\n') {
-		    //16.3.2-1 Each # preprocessing token in the replacement list for a function-like-macro shall
+
+			if (CharArrayUtils.equals( buffer, bufferPos[bufferStackPos] + 1, 11, "__VA_ARGS__".toCharArray() )) {
+				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
 		    //be followed by a parameter as the next preprocessing token 
 			if( arglist != null && !skipOverNonWhiteSpace( true ) ){
 			    ++bufferPos[bufferStackPos];  //advances us to the #
@@ -1778,7 +1786,17 @@
 				    {
 				        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] ) 
+				        			|| (arglist[i].length == 3 && arglist[i][0] == '.' && arglist[i][1] == '.' && arglist[i][2] == '.' && CharArrayUtils.equals( buffer, bufferPos[bufferStackPos], 11, "__VA_ARGS__".toCharArray() )))  
 					        {
 					            isArg = true;
 					            //advance us to the end of the arg
@@ -1790,7 +1808,7 @@
 			    }
 			    if( !isArg )
 			        handleProblem( IProblem.PREPROCESSOR_MACRO_PASTING_ERROR, bufferPos[bufferStackPos], null );
-			} else {
+	        } else {
 			    skipOverNonWhiteSpace();
 			}
 			textend = bufferPos[bufferStackPos];
@@ -1815,6 +1833,9 @@
 				? new ObjectStyleMacro(name, text)
 						: 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);
+		
 		pushCallback( getASTFactory().createMacro( name, startingOffset, startingLineNumber, idstart, idstart + idlen, nameLine, textstart + textlen, endingLine, getCurrentFilename() ) );
 	}
 	
@@ -1829,31 +1850,29 @@
 	    char[][] arglist = new char[4][];
 		int currarg = -1;
 		while (bufferPos[bufferStackPos] < limit) {
-			int pos = bufferPos[bufferStackPos];
 			skipOverWhiteSpace();
 			if (++bufferPos[bufferStackPos] >= limit)
 				return null;
 			c = buffer[bufferPos[bufferStackPos]];
+			int argstart = bufferPos[bufferStackPos];
 			if (c == ')') {
 				break;
 			} else if (c == ',') {
 				continue;
-			} else if (c == '.'
-					&& pos + 1 < limit && buffer[pos + 1] == '.'
-					&& pos + 2 < limit && buffer[pos + 2] == '.') {
-				// varargs
-				// TODO - something better
-				bufferPos[bufferStackPos] += 2;
-				arglist[++currarg] = "...".toCharArray(); //$NON-NLS-1$
-				continue;
+			} else if (c == '.' 
+	    		&& bufferPos[bufferStackPos] + 1 < limit && buffer[bufferPos[bufferStackPos] + 1] == '.' 
+	    			&& bufferPos[bufferStackPos] + 2 < limit && buffer[bufferPos[bufferStackPos] + 2] == '.') {
+					bufferPos[bufferStackPos]--; // move back and let skipOverIdentifier handle the ellipsis 
 			} else if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Character.isUnicodeIdentifierPart(c))) {
-			    if( reportProblems )
-			        handleProblem( IProblem.PREPROCESSOR_INVALID_MACRO_DEFN, idstart, name );
-				// yuck
-				skipToNewLine();
-				return null;
+		    		if( reportProblems ) {
+		    			handleProblem( IProblem.PREPROCESSOR_INVALID_MACRO_DEFN, idstart, name );
+					    
+		    			// yuck
+						skipToNewLine();
+						return null;		    		
+		    		}
 			}
-			int argstart = bufferPos[bufferStackPos];
+			
 			skipOverIdentifier();
 			if (++currarg == arglist.length) {
 				char[][] oldarglist = arglist;
@@ -1882,6 +1901,8 @@
 		{
 			if( text[i] == '\\' && i+ 1 < text.length && text[i+1] == '\n' )
 				++i;
+			else if( text[i] == '\\' && i + 1 < text.length && text[i+1] == '\r' && i + 2 < text.length && text[i+2] == '\n' )
+				i+=2;
 			else
 				result[ counter++ ] = text[i];
 		}
@@ -2247,6 +2268,14 @@
 						--bufferPos[bufferStackPos];
 						return true;
 					}
+					if( pos + 1 < limit && buffer[ pos + 1 ] == '\r')
+					{
+						if( pos + 2 < limit && buffer[ pos + 2] == '\n' )
+						{
+							bufferPos[bufferStackPos] +=2;
+							continue;
+						}
+					}
 					break;
 				case '"':
 					boolean escaped = false; 
@@ -2379,13 +2408,56 @@
 		
 		while (++bufferPos[bufferStackPos] < limit) {
 			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)) {
 				continue;
-			} 
-			break;
-
+			}
+			break; // found the end of the argument
 		}
+
 		--bufferPos[bufferStackPos];
 	}
 
@@ -2490,14 +2562,26 @@
 			    continue;
 			}
 			
-			if (++currarg >= arglist.length || arglist[currarg] == null){
-				// too many args
+			if ((++currarg >= arglist.length || arglist[currarg] == null) && !macro.hasVarArgs() && !macro.hasGCCVarArgs()) {
+				// too many args and no variable argument 
 			    handleProblem( IProblem.PREPROCESSOR_MACRO_USAGE_ERROR, bufferPos[bufferStackPos], macro.name );
 				break;
 			}
 			
 		    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;
 			int arglen = argend - argstart + 1;
@@ -2628,7 +2712,6 @@
 			char c = expansion[pos];
 			
 			if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Character.isUnicodeIdentifierPart(c)) {
-
 				wsstart = -1;
 				int idstart = pos;
 				while (++pos < limit) {

Back to the top