Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[cdt-patch] Applied [HEAD][AST2] Fw: 39688 patch (varargs)


Thanks Devin.

JohnC
www.eclipse.org/cdt

----- Forwarded by John Camelon/Ottawa/IBM on 10/20/2004 02:10 PM -----
Devin Steffler/Ottawa/IBM

10/20/2004 11:07 AM

To
John Camelon/Ottawa/IBM
cc
Subject
39688 patch (varargs)




Here you go, let me know how it goes.

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


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	20 Oct 2004 14:59:54 -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	20 Oct 2004 14:59:54 -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	20 Oct 2004 14:59:54 -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	20 Oct 2004 14:59:54 -0000
@@ -11,17 +11,61 @@
 package org.eclipse.cdt.internal.core.parser.scanner2;
 
 import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
+import org.eclipse.cdt.core.parser.util.CharArrayUtils;
 
 /**
  * @author Doug Schaefer
  */
 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;
 	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 && 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(){
@@ -52,5 +96,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.80
diff -u -r1.80 Scanner2.java
--- parser/org/eclipse/cdt/internal/core/parser/scanner2/Scanner2.java	18 Oct 2004 21:11:57 -0000	1.80
+++ parser/org/eclipse/cdt/internal/core/parser/scanner2/Scanner2.java	20 Oct 2004 14:59:55 -0000
@@ -57,6 +57,8 @@
  */
 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
 	 *
@@ -504,7 +506,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)
@@ -516,7 +518,7 @@
 					return scanString();
 					
 				case '\'':
-					return scanCharLiteral(false);
+					return scanCharLiteral();
 
 				case 'a':
 				case 'b':
@@ -1064,7 +1066,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];
@@ -1735,11 +1737,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, 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
 		    //be followed by a parameter as the next preprocessing token 
 			if( arglist != null && !skipOverNonWhiteSpace( true ) ){
 			    ++bufferPos[bufferStackPos];  //advances us to the #
@@ -1754,7 +1764,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] ) 
+				        			|| (CharArrayUtils.equals(arglist[i], ELLIPSIS_CHARARRAY) && CharArrayUtils.equals( buffer, bufferPos[bufferStackPos], VA_ARGS_CHARARRAY.length, VA_ARGS_CHARARRAY )))  
 					        {
 					            isArg = true;
 					            //advance us to the end of the arg
@@ -1791,6 +1811,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);
+		
 		callbackManager.pushCallback( getASTFactory().createMacro( name, startingOffset, startingLineNumber, idstart, idstart + idlen, nameLine, textstart + textlen, endingLine, getCurrentFilename() ) );
 	}
 	
@@ -1805,31 +1828,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;
@@ -1858,6 +1879,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];
 		}
@@ -2223,6 +2246,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; 
@@ -2355,13 +2386,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];
 	}
 
@@ -2466,14 +2540,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;
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.34
diff -u -r1.34 Scanner2Test.java
--- parser/org/eclipse/cdt/core/parser/tests/scanner2/Scanner2Test.java	19 Oct 2004 14:26:06 -0000	1.34
+++ parser/org/eclipse/cdt/core/parser/tests/scanner2/Scanner2Test.java	20 Oct 2004 15:04:06 -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
@@ -2001,5 +2003,181 @@
         validateEOF();
         
         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();
     }
 }

Back to the top