1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-07 17:56:01 +02:00

fixing macros 72506.

Also make interface IMacro, change content assist to use it.
This commit is contained in:
Andrew Niefer 2004-09-17 14:10:28 +00:00
parent 09dd8e4f22
commit 0558ecc1c9
9 changed files with 278 additions and 183 deletions

View file

@ -80,7 +80,7 @@ public class IIncludeTests extends IntegratedCModelTest {
new String("resync_after_bad_parse_3"),
new String("invalid.h"), // C-spec does not allow this, but that's OK for our present purposes
new String("myInclude1.h"),
// new String("vers2.h")
new String("vers2.h")
};
assertEquals( getIncludeNameList.length, theIncludes.length );
for( int i=0; i<getIncludeNameList.length; i++ )

View file

@ -399,4 +399,30 @@ public class CompleteParsePluginTest extends TestCase {
assertEquals( i.next(), CallbackTracker.EXIT_COMPILATION_UNIT );
assertFalse( i.hasNext() );
}
public void testBug72506() throws Exception{
String vers = "int i;\n"; //$NON-NLS-1$
String code = "#define INCFILE(x) vers ## x\n" + //$NON-NLS-1$
"#define xstr(x) str(x)\n" + //$NON-NLS-1$
"#define str(x) #x\n" + //$NON-NLS-1$
"#include xstr(INCFILE(2).h)\n"; //$NON-NLS-1$
importFile( "vers2.h", vers ); //$NON-NLS-1$
IFile cpp = importFile( "code.cpp", code ); //$NON-NLS-1$
List calls = new ArrayList();
parse( cpp, calls );
Iterator i = calls.iterator();
assertEquals( i.next(), CallbackTracker.ENTER_COMPILATION_UNIT );
assertEquals( i.next(), CallbackTracker.ACCEPT_MACRO );
assertEquals( i.next(), CallbackTracker.ACCEPT_MACRO );
assertEquals( i.next(), CallbackTracker.ACCEPT_MACRO );
assertEquals( i.next(), CallbackTracker.ENTER_INCLUSION );
assertEquals( i.next(), CallbackTracker.ACCEPT_VARIABLE );
assertEquals( i.next(), CallbackTracker.EXIT_INCLUSION );
assertEquals( i.next(), CallbackTracker.EXIT_COMPILATION_UNIT );
assertFalse( i.hasNext() );
}
}

View file

@ -1770,6 +1770,42 @@ public class Scanner2Test extends BaseScanner2Test
validateEOF();
assertTrue( callback.problems.isEmpty() );
}
}
public void testBug72506() throws Exception{
StringWriter writer = new StringWriter();
writer.write("#define INCFILE(x) ver ## x\n"); //$NON-NLS-1$
writer.write("#define xstr(x) str(x)\n"); //$NON-NLS-1$
writer.write("#define str(x) #x\n"); //$NON-NLS-1$
writer.write("xstr(INCFILE(2).h)\n"); //$NON-NLS-1$
initializeScanner( writer.toString() );
validateString("ver2.h"); //$NON-NLS-1$
validateEOF();
}
public void testBug72506_2() throws Exception{
StringWriter writer = new StringWriter();
writer.write("#define str(x) #x\n"); //$NON-NLS-1$
writer.write("#define A B\n"); //$NON-NLS-1$
writer.write("#define B A\n"); //$NON-NLS-1$
writer.write("str(B)\n"); //$NON-NLS-1$
initializeScanner( writer.toString() );
validateString( "B" ); //$NON-NLS-1$
validateEOF();
}
public void testMacroPastingError() throws Exception{
StringWriter writer = new StringWriter();
writer.write("#define m(expr) \\\r\n"); //$NON-NLS-1$
writer.write(" foo( #expr ) \r\n"); //$NON-NLS-1$
Callback callback = new Callback(ParserMode.COMPLETE_PARSE);
initializeScanner( writer.toString(), ParserMode.COMPLETE_PARSE, callback );
validateEOF();
assertTrue( callback.problems.isEmpty() );
}
}

View file

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
/*
* Created on Sep 16, 2004
*/
package org.eclipse.cdt.core.parser;
/**
* @author aniefer
*/
public interface IMacro {
//For object-like macros these will be the same,
//for function-like macros, the signature includes the parameters
public char[] getSignature();
public char[] getName();
}

View file

@ -10,11 +10,13 @@
***********************************************************************/
package org.eclipse.cdt.internal.core.parser.scanner2;
import org.eclipse.cdt.core.parser.IMacro;
/**
* @author jcamelon
*
*/
public abstract class DynamicStyleMacro {
public abstract class DynamicStyleMacro implements IMacro{
public abstract char [] execute();
@ -24,4 +26,12 @@ public abstract class DynamicStyleMacro {
}
public final char [] name;
public char[] getSignature()
{
return name;
}
public char[] getName()
{
return name;
}
}

View file

@ -18,12 +18,35 @@ import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
public class FunctionStyleMacro extends ObjectStyleMacro {
public char[][] arglist;
private char[] sig = null;
public FunctionStyleMacro(char[] name, char[] expansion, char[][] arglist) {
super(name, expansion);
this.arglist = arglist;
}
public char[] getSignature(){
if( sig != null )
return sig;
int len = name.length + 2 /*()*/;
for( int i = 0; i < arglist.length && arglist[i] != null; i++ ){
if( i + 1 < arglist.length && arglist[i+1] != null)
len += 1; /*,*/
len += arglist[i].length;
}
sig = new char[len];
System.arraycopy( name, 0, sig, 0, name.length );
sig[name.length] = '(';
int idx = name.length + 1;
for( int i = 0; i < arglist.length && arglist[i] != null; i++ ){
System.arraycopy( arglist[i], 0, sig, idx, arglist[i].length );
idx += arglist[i].length;
if( i + 1 < arglist.length && arglist[i+1] != null )
sig[idx++] = ',';
}
sig[idx] = ')';
return sig;
}
public class Expansion {
public final CharArrayObjectMap definitions

View file

@ -10,10 +10,12 @@
******************************************************************************/
package org.eclipse.cdt.internal.core.parser.scanner2;
import org.eclipse.cdt.core.parser.IMacro;
/**
* @author Doug Schaefer
*/
public class ObjectStyleMacro {
public class ObjectStyleMacro implements IMacro{
public char[] name;
public char[] expansion;
@ -22,4 +24,12 @@ public class ObjectStyleMacro {
this.name = name;
this.expansion = expansion;
}
public char[] getSignature() {
return name;
}
public char[] getName(){
return name;
}
}

View file

@ -21,6 +21,7 @@ import java.util.Map;
import org.eclipse.cdt.core.parser.CodeReader;
import org.eclipse.cdt.core.parser.EndOfFileException;
import org.eclipse.cdt.core.parser.IMacro;
import org.eclipse.cdt.core.parser.IParserLogService;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.core.parser.IScanner;
@ -835,16 +836,13 @@ public class Scanner2 implements IScanner, IScannerData {
private IToken newToken( int signal, char [] buffer )
{
if( bufferData[bufferStackPos] instanceof ObjectStyleMacro ||
bufferData[bufferStackPos] instanceof FunctionStyleMacro )
if( bufferData[bufferStackPos] instanceof IMacro )
{
int mostRelevant;
for( mostRelevant = bufferStackPos; mostRelevant >= 0; --mostRelevant )
if( bufferData[mostRelevant] instanceof InclusionData || bufferData[mostRelevant] instanceof CodeReader )
break;
if( bufferData[bufferStackPos] instanceof ObjectStyleMacro )
return new ImagedExpansionToken( signal, buffer, bufferPos[mostRelevant], ((ObjectStyleMacro)bufferData[bufferStackPos]).name.length, getCurrentFilename(), getLineNumber( bufferPos[bufferStackPos] + 1) );
return new ImagedExpansionToken( signal, buffer, bufferPos[mostRelevant], ((FunctionStyleMacro)bufferData[bufferStackPos]).name.length, getCurrentFilename(), getLineNumber( bufferPos[bufferStackPos] + 1));
return new ImagedExpansionToken( signal, buffer, bufferPos[mostRelevant], ((IMacro)bufferData[bufferStackPos]).getName().length, getCurrentFilename(), getLineNumber( bufferPos[bufferStackPos] + 1));
}
IToken i = new ImagedToken(signal, buffer, bufferPos[bufferStackPos] + 1 , getCurrentFilename(), getLineNumber( bufferPos[bufferStackPos] + 1));
if( buffer != null && buffer.length == 0 )
@ -896,19 +894,7 @@ public class Scanner2 implements IScanner, IScannerData {
// Check for macro expansion
Object expObject = definitions.get(buffer, start, len);
// but not if it has been expanded on the stack already
// i.e. recursion avoidance
if (expObject != null && !isLimitReached() )
for (int stackPos = bufferStackPos; stackPos >= 0; --stackPos)
if (bufferData[stackPos] != null
&& bufferData[stackPos] instanceof ObjectStyleMacro
&& CharArrayUtils.equals(buffer, start, len,
((ObjectStyleMacro)bufferData[stackPos]).name)) {
expObject = null;
break;
}
if (expObject != null && !isLimitReached()) {
if (expObject != null && !isLimitReached() && shouldExpandMacro( (IMacro) expObject)) {
if (expObject instanceof FunctionStyleMacro) {
handleFunctionStyleMacro((FunctionStyleMacro)expObject, true);
} else if (expObject instanceof ObjectStyleMacro) {
@ -947,6 +933,26 @@ public class Scanner2 implements IScanner, IScannerData {
return newToken(tokenType);
}
/**
* @param buffer
* @param start
* @param len
* @param expObject
* @return
*/
private boolean shouldExpandMacro( IMacro macro ) {
// but not if it has been expanded on the stack already
// i.e. recursion avoidance
if( macro != null && !isLimitReached() )
for (int stackPos = bufferStackPos; stackPos >= 0; --stackPos)
if( bufferData[stackPos] != null && bufferData[stackPos] instanceof IMacro &&
CharArrayUtils.equals(macro.getName(), ((IMacro)bufferData[stackPos]).getName()) )
{
return false;
}
return true;
}
/**
* @return
*/
@ -1521,6 +1527,7 @@ public class Scanner2 implements IScanner, IScannerData {
}
if( t != null )
{
t = replaceArgumentMacros( t );
if( (t[ t.length - 1 ] == t[0] ) && ( t[0] == '\"') )
{
local = true;
@ -1702,14 +1709,19 @@ public class Scanner2 implements IScanner, IScannerData {
boolean encounteredMultilineComment = 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
//be followed by a parameter as the next preprocessing token
if( arglist != null && !skipOverNonWhiteSpace( true ) ){
++bufferPos[bufferStackPos];
++bufferPos[bufferStackPos]; //advances us to the #
if( skipOverWhiteSpace() )
encounteredMultilineComment = true;
++bufferPos[bufferStackPos]; //advances us past the # (or last whitespace)
boolean isArg = false;
for( int i = 0; i < arglist.length && arglist[i] != null; i++ ){
if( CharArrayUtils.equals( buffer, bufferPos[bufferStackPos], arglist[i].length, arglist[i] ) ){
isArg = true;
//advance us to the end of the arg
bufferPos[bufferStackPos] += arglist[i].length - 1;
break;
}
}
@ -2145,29 +2157,27 @@ public class Scanner2 implements IScanner, IScannerData {
return true;
}
private void skipOverMacroArg() {
char[] buffer = bufferStack[bufferStackPos];
private int skipOverMacroArg(){
char [] buffer = bufferStack[bufferStackPos];
int limit = bufferLimit[bufferStackPos];
int argEnd = bufferPos[bufferStackPos]--;
int nesting = 0;
while (++bufferPos[bufferStackPos] < limit) {
switch (buffer[bufferPos[bufferStackPos]]) {
case ' ':
case '\t':
case '\r':
case '\n':
case ',':
case ')':
case '(':
case '<':
case '>':
++nesting;
break;
case ')':
if( nesting == 0 ){
--bufferPos[bufferStackPos];
return;
case '\\':
int pos = bufferPos[bufferStackPos];
if (pos + 1 < limit && buffer[pos + 1] == '\n') {
// \n is whitespace
return argEnd;
}
--nesting;
break;
case ',':
if( nesting == 0 ){
--bufferPos[bufferStackPos];
return;
return argEnd;
}
break;
case '"':
@ -2190,8 +2200,11 @@ public class Scanner2 implements IScanner, IScannerData {
}
break;
}
argEnd = bufferPos[bufferStackPos];
skipOverWhiteSpace();
}
--bufferPos[bufferStackPos];
return argEnd;
}
private void skipOverIdentifier() {
@ -2274,73 +2287,23 @@ public class Scanner2 implements IScanner, IScannerData {
CharArrayObjectMap argmap = new CharArrayObjectMap(arglist.length);
while (bufferPos[bufferStackPos] < limit) {
skipOverWhiteSpace();
if (buffer[++bufferPos[bufferStackPos]] == ')') {
// end of macro
break;
} else if( buffer[bufferPos[bufferStackPos]] == ',' ){
continue;
}
if (++currarg >= arglist.length || arglist[currarg] == null){
// too many args
handleProblem( IProblem.PREPROCESSOR_MACRO_USAGE_ERROR, bufferPos[bufferStackPos], macro.name );
break;
}
skipOverWhiteSpace();
int pos = ++bufferPos[bufferStackPos];
char c = buffer[pos];
if (c == ')') {
// end of macro
break;
} else if (c == ',') {
// empty arg
argmap.put(arglist[currarg], emptyCharArray);
continue;
}
// peel off the arg
--bufferPos[bufferStackPos];
int argend = bufferPos[bufferStackPos];
int argstart = argend + 1;
// Loop looking for end of argument
int argparens = 0;
// int startOffset = -1;
while (bufferPos[bufferStackPos] < limit) {
// if( bufferPos[bufferStackPos] == startOffset )
// ++bufferPos[bufferStackPos];
// startOffset = bufferPos[bufferStackPos];
skipOverMacroArg();
argend = bufferPos[bufferStackPos];
skipOverWhiteSpace();
if (++bufferPos[bufferStackPos] >= limit)
break;
c = buffer[bufferPos[bufferStackPos]];
if (c == '(' || c == '<')
++argparens;
else if (c == '>')
--argparens;
else if (c == ')') {
if (argparens == 0)
break;
--argparens;
} else if (c == ',') {
if (argparens == 0) {
break;
}
} else if (c == '\n') {
// eat it and continue
continue;
} else {
// start of next macro arg
--bufferPos[bufferStackPos];
continue;
}
skipOverWhiteSpace();
while (++bufferPos[bufferStackPos] < limit) {
if (buffer[bufferPos[bufferStackPos]] != '\n')
break;
skipOverWhiteSpace();
}
--bufferPos[bufferStackPos];
}
int argstart = bufferPos[bufferStackPos];
int argend = skipOverMacroArg();
char[] arg = emptyCharArray;
int arglen = argend - argstart + 1;
@ -2349,15 +2312,11 @@ public class Scanner2 implements IScanner, IScannerData {
System.arraycopy(buffer, argstart, arg, 0, arglen);
}
//TODO 16.3.1 We are supposed to completely macro replace the arguments before
//substituting them in, this is only a partial replacement of object macros,
//we may need a full solution later.
//16.3.1 completely macro replace the arguments before substituting them in
arg = replaceArgumentMacros( arg );
argmap.put(arglist[currarg], arg);
if (c == ')')
break;
}
int numArgs = arglist.length;
for( int i = 0; i < arglist.length; i++ ){
if( arglist[i] == null ){
@ -2378,33 +2337,74 @@ public class Scanner2 implements IScanner, IScannerData {
}
private char[] replaceArgumentMacros( char [] arg ){
// Check for macro expansion
Object expObject = definitions.get(arg, 0, arg.length);
// but not if it has been expanded on the stack already
// i.e. recursion avoidance
if (expObject != null){
for (int stackPos = bufferStackPos; stackPos >= 0; --stackPos){
if (bufferData[stackPos] != null
&& bufferData[stackPos] instanceof ObjectStyleMacro
&& CharArrayUtils.equals(arg, ((ObjectStyleMacro)bufferData[stackPos]).name))
int limit = arg.length;
int start = -1, end = -1;
Object expObject = null;
for( int pos = 0; pos < limit; pos++ ){
char c = arg[pos];
if( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' ||
Character.isLetter( c ) || scannerExtension.isValidIdentifierStartCharacter( c ) )
{
expObject = null;
start = pos;
while (++pos < limit) {
c = arg[pos];
if( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9') ||
scannerExtension.isValidIdentifierCharacter(c) || Character.isUnicodeIdentifierPart(c) )
{
continue;
}
break;
}
end = pos - 1;
}
if( start != -1 && end >= start ){
//Check for macro expansion
expObject = definitions.get(arg, start, ( end - start + 1 ) );
if( expObject == null || !shouldExpandMacro( (IMacro) expObject) ){
expObject = null;
start = -1;
continue;
}
//else, break and expand macro
break;
}
}
if( expObject == null )
return arg;
if (expObject instanceof ObjectStyleMacro) {
char [] expansion = null;
if( expObject instanceof FunctionStyleMacro ){
FunctionStyleMacro expMacro = (FunctionStyleMacro) expObject;
pushContext( ( start == 0 ) ? arg : CharArrayUtils.extract( arg, start, arg.length - start + 1 ) );
bufferPos[bufferStackPos] += end - start + 1;
expansion = handleFunctionStyleMacro( expMacro, false );
end = bufferPos[bufferStackPos];
popContext();
} else if (expObject instanceof ObjectStyleMacro) {
ObjectStyleMacro expMacro = (ObjectStyleMacro)expObject;
return expMacro.expansion;
expansion = expMacro.expansion;
} else if (expObject instanceof char[]) {
return (char[])expObject;
expansion = (char[])expObject;
} else if( expObject instanceof DynamicStyleMacro ){
DynamicStyleMacro expMacro = (DynamicStyleMacro) expObject;
expansion = expMacro.execute();
}
if( expansion != null ){
int newlength = start + expansion.length + ( limit - end - 1 );
char [] result = new char [ newlength ];
System.arraycopy( arg, 0, result, 0, start);
System.arraycopy( expansion, 0, result, start, expansion.length );
if( arg.length > end + 1 )
System.arraycopy( arg, end + 1, result, start + expansion.length, limit - end - 1 );
//we need to put the macro on the context stack in order to detect recursive macros
pushContext( emptyCharArray, expObject );
arg = replaceArgumentMacros( result ); //rescan for more macros
popContext();
}
return arg;
}

View file

@ -14,15 +14,13 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.core.parser.CodeReader;
import org.eclipse.cdt.core.parser.IMacro;
import org.eclipse.cdt.core.parser.IParser;
import org.eclipse.cdt.core.parser.IScanner;
import org.eclipse.cdt.core.parser.IScannerInfo;
@ -57,9 +55,9 @@ import org.eclipse.cdt.core.parser.ast.IASTVariable;
import org.eclipse.cdt.core.parser.ast.IASTCompletionNode.CompletionKind;
import org.eclipse.cdt.core.parser.ast.IASTNode.ILookupResult;
import org.eclipse.cdt.core.parser.ast.IASTNode.LookupKind;
import org.eclipse.cdt.core.parser.util.CharArrayObjectMap;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.CharOperation;
import org.eclipse.cdt.internal.core.parser.scanner2.FunctionStyleMacro;
import org.eclipse.cdt.internal.core.parser.scanner2.ObjectStyleMacro;
import org.eclipse.cdt.internal.ui.CUIMessages;
import org.eclipse.cdt.internal.ui.util.IDebugLogConstants;
import org.eclipse.cdt.internal.ui.util.Util;
@ -82,7 +80,7 @@ public class CompletionEngine implements RelevanceConstants {
int completionLength = 0;
int completionOrigin = 0;
IPreferenceStore store = CUIPlugin.getDefault().getPreferenceStore();
private Map macroMap = null;
private CharArrayObjectMap macroMap = null;
private ContentAssistElementRequestor elementRequestor = null;
private static final String exceptionKeyword = "..."; //$NON-NLS-1$
@ -190,7 +188,7 @@ public class CompletionEngine implements RelevanceConstants {
result = parser.parse(completionOffset);
log("Time spent in Parser = "+ ( System.currentTimeMillis() - parserTime ) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
macroMap = scanner.getDefinitions();
macroMap = scanner.getRealDefinitions();
} catch (ParseError e ) {
if(e.getErrorKind() == ParseError.ParseErrorKind.TIMEOUT_OR_CANCELLED){
log("Timeout received !!!!!! "); //$NON-NLS-1$;
@ -443,54 +441,21 @@ public class CompletionEngine implements RelevanceConstants {
}
private List lookupMacros(String prefix){
Set keySet = new TreeSet(macroMap.keySet());
Iterator i = keySet.iterator();
//simply doing a linear search on the keys will be faster than sorting them
//and then searching the sorted list.
char [] prefixArray = prefix.toCharArray();
char [] key;
final int length = prefix.length();
String newPrefix = prefix.toUpperCase();
List resultSet = new ArrayList();
String key = null;
String value = null; //$NON-NLS-1$
while( i.hasNext() )
{
key = (String) i.next();
if( key.length() < length )
for( int i = 0; i < macroMap.size(); i++ ){
key = macroMap.keyAt( i );
if( key.length < length )
continue;
if(key.length() > length) {
value = key.substring(0, length).toUpperCase();
}else {
value = key;
if( CharArrayUtils.equals( key, 0, length, prefixArray, true ) ){
IMacro macro = (IMacro) macroMap.getAt( i );
resultSet.add( String.valueOf( macro.getSignature() ) );
}
if( value.equals( newPrefix ) ) {
Object macroD = macroMap.get(key);
if( macroD instanceof FunctionStyleMacro )
{
FunctionStyleMacro f = ((FunctionStyleMacro)macroD);
StringBuffer buffer = new StringBuffer( String.valueOf( f.name ));
buffer.append( "("); //$NON-NLS-1$
if( f.arglist != null )
{
for( int j = 0; j < f.arglist.length; ++j )
{
if( f.arglist[j] != null )
buffer.append( f.arglist[j]);
if( j != f.arglist.length -1 && f.arglist[j+1] != null )
buffer.append( ","); //$NON-NLS-1$
}
}
buffer.append( ")"); //$NON-NLS-1$
String result = buffer.toString();
resultSet.add( result );
}
else if (macroD instanceof ObjectStyleMacro )
{
String v = String.valueOf( ((ObjectStyleMacro)macroD).name);
resultSet.add( v );
}
}
else if( key.compareTo( prefix ) > 0 )
break;
}
return resultSet;
}