mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-31 21:05:37 +02:00
Bug 275382. Fix and test case.
This commit is contained in:
parent
e8622b71d8
commit
6a9e1ff1dd
4 changed files with 182 additions and 5 deletions
|
@ -1313,6 +1313,9 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable {
|
||||||
final char[] namechars= name.getCharImage();
|
final char[] namechars= name.getCharImage();
|
||||||
macro= fMacroDictionary.get(namechars);
|
macro= fMacroDictionary.get(namechars);
|
||||||
isActive= (macro == null) == isIfndef;
|
isActive= (macro == null) == isIfndef;
|
||||||
|
if (macro == null) {
|
||||||
|
macro = new UndefinedMacro(namechars);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ abstract class PreprocessorMacro implements IMacroBinding {
|
||||||
buf.append(getNameCharArray());
|
buf.append(getNameCharArray());
|
||||||
buf.append('(');
|
buf.append('(');
|
||||||
for (int i = 0; i < p.length; i++) {
|
for (int i = 0; i < p.length; i++) {
|
||||||
if (i>0) {
|
if (i > 0) {
|
||||||
buf.append(',');
|
buf.append(',');
|
||||||
}
|
}
|
||||||
buf.append(p[i]);
|
buf.append(p[i]);
|
||||||
|
@ -271,6 +271,29 @@ class FunctionStyleMacro extends ObjectStyleMacro {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class UndefinedMacro extends PreprocessorMacro {
|
||||||
|
public UndefinedMacro(char[] name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TokenList getTokens(MacroDefinitionParser parser, LexerOptions lexOptions, MacroExpander expander) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char[] getExpansion() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char[] getExpansionImage() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDynamic() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract class DynamicMacro extends PreprocessorMacro {
|
abstract class DynamicMacro extends PreprocessorMacro {
|
||||||
|
|
||||||
public DynamicMacro(char[] name) {
|
public DynamicMacro(char[] name) {
|
||||||
|
@ -351,11 +374,13 @@ final class LineMacro extends DynamicMacro {
|
||||||
LineMacro(char[] name) {
|
LineMacro(char[] name) {
|
||||||
super(name);
|
super(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Token execute(MacroExpander expander) {
|
public Token execute(MacroExpander expander) {
|
||||||
int lineNumber= expander.getCurrentLineNumber();
|
int lineNumber= expander.getCurrentLineNumber();
|
||||||
return new TokenWithImage(IToken.tINTEGER, null, 0, 0, Long.toString(lineNumber).toCharArray());
|
return new TokenWithImage(IToken.tINTEGER, null, 0, 0, Long.toString(lineNumber).toCharArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public char[] getExpansionImage() {
|
public char[] getExpansionImage() {
|
||||||
return new char[] {'1'};
|
return new char[] {'1'};
|
||||||
}
|
}
|
||||||
|
@ -387,4 +412,3 @@ final class TimeMacro extends DynamicMacro {
|
||||||
return createDate();
|
return createDate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,4 +123,17 @@ public class LinkedNamesFinderTest extends AST2BaseTest {
|
||||||
IRegion[] regions3 = getLinkedRegions(code, "~A();", 2, true);
|
IRegion[] regions3 = getLinkedRegions(code, "~A();", 2, true);
|
||||||
assertTrue(Arrays.equals(regions3, regions));
|
assertTrue(Arrays.equals(regions3, regions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #ifndef GUARD //1
|
||||||
|
// #define GUARD //2
|
||||||
|
// // This is a GUARD test
|
||||||
|
// #endif // GUARD
|
||||||
|
public void testIncludeGuards() throws Exception {
|
||||||
|
String code = getAboveComment();
|
||||||
|
IRegion[] regions = getLinkedRegions(code, "GUARD //1", 5, true);
|
||||||
|
assertEquals(3, regions.length);
|
||||||
|
assertContents(code, regions[0].getOffset(), "GUARD //1");
|
||||||
|
assertContents(code, regions[1].getOffset(), "GUARD //2");
|
||||||
|
assertContents(code, regions[2].getOffset() - 3, "// GUARD");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,28 +11,41 @@
|
||||||
package org.eclipse.cdt.internal.ui.search;
|
package org.eclipse.cdt.internal.ui.search;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jface.text.IRegion;
|
import org.eclipse.jface.text.IRegion;
|
||||||
import org.eclipse.jface.text.Region;
|
import org.eclipse.jface.text.Region;
|
||||||
|
|
||||||
import org.eclipse.cdt.core.dom.ast.DOMException;
|
import org.eclipse.cdt.core.dom.ast.DOMException;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IASTComment;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
|
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTName;
|
import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElseStatement;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorEndifStatement;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfStatement;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfdefStatement;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfndefStatement;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorUndefStatement;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||||
import org.eclipse.cdt.core.dom.ast.IBinding;
|
import org.eclipse.cdt.core.dom.ast.IBinding;
|
||||||
|
import org.eclipse.cdt.core.dom.ast.IMacroBinding;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds locations of linked names. Used by Rename in File.
|
* Finds locations of linked names. Used by Rename in File.
|
||||||
*/
|
*/
|
||||||
public class LinkedNamesFinder {
|
public class LinkedNamesFinder {
|
||||||
private static final IRegion[] EMPTY_LOCATIONS_ARRAY = new IRegion[0];
|
private static final IRegion[] EMPTY_LOCATIONS_ARRAY = new IRegion[0];
|
||||||
|
|
||||||
public LinkedNamesFinder() {
|
private LinkedNamesFinder() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,13 +62,18 @@ public class LinkedNamesFinder {
|
||||||
private static class BindingFinder {
|
private static class BindingFinder {
|
||||||
private final IASTTranslationUnit root;
|
private final IASTTranslationUnit root;
|
||||||
private final List<IRegion> locations;
|
private final List<IRegion> locations;
|
||||||
|
|
||||||
public BindingFinder(IASTTranslationUnit root) {
|
public BindingFinder(IASTTranslationUnit root) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
locations = new ArrayList<IRegion>();
|
locations = new ArrayList<IRegion>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void find(IBinding target) {
|
public void find(IBinding target) {
|
||||||
|
if (target instanceof IMacroBinding) {
|
||||||
|
findMacro((IMacroBinding) target);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (target instanceof ICPPConstructor ||
|
if (target instanceof ICPPConstructor ||
|
||||||
target instanceof ICPPMethod && ((ICPPMethod) target).isDestructor()) {
|
target instanceof ICPPMethod && ((ICPPMethod) target).isDestructor()) {
|
||||||
|
@ -132,5 +150,124 @@ public class LinkedNamesFinder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all occurrences of a macro name to the list of locations. Macro occurrences
|
||||||
|
* may belong to multiple macro bindings with the same name. Macro names are also
|
||||||
|
* looked for in the comments of #else and #endif statements.
|
||||||
|
* Comments of #else and #endif statements related to #ifdef or #ifndef are searched
|
||||||
|
* for the macro name referenced by the #if[n]def.
|
||||||
|
* @param target a binding representing a macro.
|
||||||
|
*/
|
||||||
|
private void findMacro(IMacroBinding target) {
|
||||||
|
findBinding(target);
|
||||||
|
char[] nameChars = target.getNameCharArray();
|
||||||
|
List<IASTName> ifdefNameStack = new ArrayList<IASTName>();
|
||||||
|
IASTPreprocessorStatement[] statements = root.getAllPreprocessorStatements();
|
||||||
|
for (IASTPreprocessorStatement statement : statements) {
|
||||||
|
if (!statement.isPartOfTranslationUnitFile()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
IASTName macroName = null;
|
||||||
|
boolean ifStatement = false;
|
||||||
|
if (statement instanceof IASTPreprocessorIfdefStatement) {
|
||||||
|
macroName = ((IASTPreprocessorIfdefStatement) statement).getMacroReference();
|
||||||
|
ifStatement = true;
|
||||||
|
} else if (statement instanceof IASTPreprocessorIfndefStatement) {
|
||||||
|
macroName = ((IASTPreprocessorIfndefStatement) statement).getMacroReference();
|
||||||
|
ifStatement = true;
|
||||||
|
} else if (statement instanceof IASTPreprocessorMacroDefinition) {
|
||||||
|
macroName = ((IASTPreprocessorMacroDefinition) statement).getName();
|
||||||
|
} else if (statement instanceof IASTPreprocessorUndefStatement) {
|
||||||
|
macroName = ((IASTPreprocessorUndefStatement) statement).getMacroName();
|
||||||
|
} else if (statement instanceof IASTPreprocessorIfStatement) {
|
||||||
|
ifStatement = true;
|
||||||
|
} else if (statement instanceof IASTPreprocessorEndifStatement) {
|
||||||
|
if (!ifdefNameStack.isEmpty())
|
||||||
|
if (ifdefNameStack.remove(ifdefNameStack.size() - 1) != null) {
|
||||||
|
findInStatementComment(nameChars, statement);
|
||||||
|
}
|
||||||
|
} else if (statement instanceof IASTPreprocessorElseStatement) {
|
||||||
|
if (!ifdefNameStack.isEmpty())
|
||||||
|
if (ifdefNameStack.get(ifdefNameStack.size() - 1) != null) {
|
||||||
|
findInStatementComment(nameChars, statement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (macroName != null) {
|
||||||
|
if (Arrays.equals(nameChars, macroName.getSimpleID())) {
|
||||||
|
IBinding binding = macroName.resolveBinding();
|
||||||
|
if (!target.equals(binding)) {
|
||||||
|
findBinding(binding);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
macroName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ifStatement) {
|
||||||
|
ifdefNameStack.add(macroName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds locations of a given name in the comment of a preprocessor statement.
|
||||||
|
*/
|
||||||
|
private void findInStatementComment(char[] nameChars, IASTPreprocessorStatement statement) {
|
||||||
|
IASTFileLocation location = statement.getFileLocation();
|
||||||
|
IASTComment comment = findComment(location.getNodeOffset() + location.getNodeLength());
|
||||||
|
if (comment != null &&
|
||||||
|
comment.getFileLocation().getStartingLineNumber() == location.getStartingLineNumber()) {
|
||||||
|
findInComment(nameChars, comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first comment after the given offset.
|
||||||
|
* @param startOffset a file offset.
|
||||||
|
* @return a comment or <code>null</code>, if there are no comments after the offset.
|
||||||
|
*/
|
||||||
|
private IASTComment findComment(int startOffset) {
|
||||||
|
IASTComment[] comments = ((ASTTranslationUnit) root).getComments();
|
||||||
|
int low = 0;
|
||||||
|
int high = comments.length;
|
||||||
|
while (low < high) {
|
||||||
|
int mid = (low + high) / 2;
|
||||||
|
int offset = comments[mid].getFileLocation().getNodeOffset();
|
||||||
|
if (offset < startOffset) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else {
|
||||||
|
high = mid;
|
||||||
|
if (offset == startOffset) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return high < comments.length ? comments[high] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all occurrences of a name in a comment to the list of locations.
|
||||||
|
*/
|
||||||
|
private void findInComment(char[] name, IASTComment comment) {
|
||||||
|
char[] text = comment.getComment();
|
||||||
|
int j = 0;
|
||||||
|
// First two characters are either /* or //
|
||||||
|
for (int i = 2; i <= text.length - name.length + j; i++) {
|
||||||
|
char c = text[i];
|
||||||
|
if (!Character.isJavaIdentifierPart(c)) {
|
||||||
|
j = 0;
|
||||||
|
} else if (j >= 0 && j < name.length && name[j] == c) {
|
||||||
|
j++;
|
||||||
|
if (j == name.length &&
|
||||||
|
(i + 1 == text.length || !Character.isJavaIdentifierPart(text[i + 1]))) {
|
||||||
|
int offset = comment.getFileLocation().getNodeOffset() + i + 1 - name.length;
|
||||||
|
locations.add(new Region(offset, name.length));
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
j = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue