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

Bug 508216 - Enclosing definition for macro reference name

Compute and store in the index an enclosing definition for
macro reference names. This allows showing the enclosing
definition when such a name appears in a search result.

Change-Id: I91eee4ad80c86d7ef90c69c1436387393fca2a19
This commit is contained in:
Nathan Ridge 2016-12-12 01:21:35 -05:00
parent e31d51988d
commit cf46cbf117
7 changed files with 118 additions and 20 deletions

View file

@ -278,10 +278,13 @@ public class PDOM extends PlatformObject implements IPDOM {
* 204.0 - Do not store return expression in index, follow-up to bug 490475. * 204.0 - Do not store return expression in index, follow-up to bug 490475.
* 205.0 - Reworked storage of annotations, bug 505832. * 205.0 - Reworked storage of annotations, bug 505832.
* 206.0 - DependentValue split out from IntegralValue. * 206.0 - DependentValue split out from IntegralValue.
*
* CDT 9.3 development (versions not supported on the 9.2.x branch)
* 207.0 - Store a caller record for macro reference names.
*/ */
private static final int MIN_SUPPORTED_VERSION= version(206, 0); private static final int MIN_SUPPORTED_VERSION= version(207, 0);
private static final int MAX_SUPPORTED_VERSION= version(206, Short.MAX_VALUE); private static final int MAX_SUPPORTED_VERSION= version(207, Short.MAX_VALUE);
private static final int DEFAULT_VERSION = version(206, 0); private static final int DEFAULT_VERSION = version(207, 0);
private static int version(int major, int minor) { private static int version(int major, int minor) {
return (major << 16) + minor; return (major << 16) + minor;

View file

@ -505,6 +505,11 @@ public abstract class PDOMWriter implements IPDOMASTProcessor {
ast.accept(visitor); ast.accept(visitor);
if ((fSkipReferences & SKIP_MACRO_REFERENCES) == 0) { if ((fSkipReferences & SKIP_MACRO_REFERENCES) == 0) {
// Get a tree of definitions built by IndexerASTVisitor during its traversal.
// This is used to find enclosing definitions for macro references.
IndexerASTVisitor.Definition definitionTree = visitor.getDefinitionTree();
LocationMap lm= ast.getAdapter(LocationMap.class); LocationMap lm= ast.getAdapter(LocationMap.class);
if (lm != null) { if (lm != null) {
IASTName[] refs= lm.getMacroReferences(); IASTName[] refs= lm.getMacroReferences();
@ -512,7 +517,9 @@ public abstract class PDOMWriter implements IPDOMASTProcessor {
IASTFileLocation nameLoc = name.getFileLocation(); IASTFileLocation nameLoc = name.getFileLocation();
if (nameLoc != null) { if (nameLoc != null) {
IASTPreprocessorIncludeStatement owner= nameLoc.getContextInclusionStatement(); IASTPreprocessorIncludeStatement owner= nameLoc.getContextInclusionStatement();
symbols.add(owner, name, null); IASTName enclosingDefinition = definitionTree.search(nameLoc.getNodeOffset(),
nameLoc.getNodeLength());
symbols.add(owner, name, enclosingDefinition);
} }
} }
} }

View file

@ -510,7 +510,7 @@ public class PDOMFile implements IIndexFragmentFile {
try { try {
if (binding instanceof IMacroBinding if (binding instanceof IMacroBinding
|| (binding == null && name.getPropertyInParent() == IASTPreprocessorStatement.MACRO_NAME)) { || (binding == null && name.getPropertyInParent() == IASTPreprocessorStatement.MACRO_NAME)) {
return createPDOMMacroReferenceName(linkage, name); return createPDOMMacroReferenceName(linkage, name, caller);
} }
PDOMBinding pdomBinding = linkage.addBinding(name); PDOMBinding pdomBinding = linkage.addBinding(name);
if (pdomBinding != null) { if (pdomBinding != null) {
@ -529,9 +529,10 @@ public class PDOMFile implements IIndexFragmentFile {
return null; return null;
} }
private IIndexFragmentName createPDOMMacroReferenceName(PDOMLinkage linkage, IASTName name) throws CoreException { private IIndexFragmentName createPDOMMacroReferenceName(PDOMLinkage linkage, IASTName name,
PDOMName caller) throws CoreException {
PDOMMacroContainer cont= linkage.getMacroContainer(name.getSimpleID()); PDOMMacroContainer cont= linkage.getMacroContainer(name.getSimpleID());
return new PDOMMacroReferenceName(fLinkage, name, this, cont); return new PDOMMacroReferenceName(fLinkage, name, this, cont, caller);
} }
public void clear() throws CoreException { public void clear() throws CoreException {

View file

@ -42,11 +42,12 @@ public final class PDOMMacroReferenceName implements IIndexFragmentName, IASTFil
private static final int CONTAINER_NEXT_OFFSET = 16; private static final int CONTAINER_NEXT_OFFSET = 16;
private static final int NODE_OFFSET_OFFSET = 20; private static final int NODE_OFFSET_OFFSET = 20;
private static final int NODE_LENGTH_OFFSET = 24; private static final int NODE_LENGTH_OFFSET = 24;
private static final int CALLER_REC_OFFSET = 26;
private static final int RECORD_SIZE = 26; private static final int RECORD_SIZE = 30; // 30 yields a 32-byte block. (31 would trigger a 40-byte block)
public PDOMMacroReferenceName(PDOMLinkage linkage, IASTName name, PDOMFile file, public PDOMMacroReferenceName(PDOMLinkage linkage, IASTName name, PDOMFile file,
PDOMMacroContainer container) throws CoreException { PDOMMacroContainer container, PDOMName caller) throws CoreException {
this.linkage = linkage; this.linkage = linkage;
Database db = linkage.getDB(); Database db = linkage.getDB();
record = db.malloc(RECORD_SIZE); record = db.malloc(RECORD_SIZE);
@ -59,6 +60,10 @@ public final class PDOMMacroReferenceName implements IIndexFragmentName, IASTFil
db.putInt(record + NODE_OFFSET_OFFSET, fileloc.getNodeOffset()); db.putInt(record + NODE_OFFSET_OFFSET, fileloc.getNodeOffset());
db.putShort(record + NODE_LENGTH_OFFSET, (short) fileloc.getNodeLength()); db.putShort(record + NODE_LENGTH_OFFSET, (short) fileloc.getNodeLength());
container.addReference(this); container.addReference(this);
if (caller != null) {
db.putRecPtr(record + CALLER_REC_OFFSET, caller.getRecord());
}
} }
public PDOMMacroReferenceName(PDOMLinkage linkage, long nameRecord) { public PDOMMacroReferenceName(PDOMLinkage linkage, long nameRecord) {
@ -296,6 +301,7 @@ public final class PDOMMacroReferenceName implements IIndexFragmentName, IASTFil
@Override @Override
public IIndexName getEnclosingDefinition() throws CoreException { public IIndexName getEnclosingDefinition() throws CoreException {
return null; long namerec = linkage.getDB().getRecPtr(record + CALLER_REC_OFFSET);
return namerec != 0 ? new PDOMName(linkage, namerec) : null;
} }
} }

View file

@ -35,17 +35,54 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLambdaExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLambdaExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries; import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
abstract public class IndexerASTVisitor extends ASTVisitor { abstract public class IndexerASTVisitor extends ASTVisitor {
private static class Definition { /**
* Represents a definition of a class or function.
* IndexerASTVisitor builds a tree of these definitions, used for tracking enclosing
* definitions of names.
*/
public static class Definition {
Definition(IASTName name, IASTNode node) { Definition(IASTName name, IASTNode node) {
fName= name; fName= name;
fNode= node; fNode= node;
} }
IASTName fName; IASTName fName; // The name of the entity being defined.
IASTNode fNode; IASTNode fNode; // The AST node for the entire definition.
List<Definition> fChildren; // Definitions contained within this one.
/**
* Search the subtree of definitions rooted at this one for the nearest
* definition that encloses the range defined by 'offset' and 'length'.
* The name of the resulting definition is returned.
* This function assumes that 'this.matches(offset, length)' is true.
*/
public IASTName search(int offset, int length) {
if (fChildren != null) {
for (Definition child : fChildren) {
if (child.matches(offset, length)) {
return child.search(offset, length);
}
}
}
return fName;
}
/**
* Check whether this definition encloses the range defined by 'offset' and 'length'.
*/
boolean matches(int offset, int length) {
if (!(fNode instanceof ASTNode)) {
return false;
}
ASTNode node = (ASTNode) fNode;
int nodeOffset = node.getOffset();
int nodeLength = node.getLength();
return nodeOffset <= offset && (nodeOffset + nodeLength) >= (offset + length);
}
} }
private IASTName fDefinitionName; private IASTName fDefinitionName;
@ -61,6 +98,9 @@ abstract public class IndexerASTVisitor extends ASTVisitor {
shouldVisitDeclSpecifiers= true; shouldVisitDeclSpecifiers= true;
shouldVisitProblems= true; shouldVisitProblems= true;
shouldVisitExpressions= true; shouldVisitExpressions= true;
// Root node representing the entire file
fStack.add(new Definition(null, null));
} }
public List<IASTProblem> getProblems() { public List<IASTProblem> getProblems() {
@ -80,9 +120,14 @@ abstract public class IndexerASTVisitor extends ASTVisitor {
} }
private void push(IASTName name, IASTNode node) { private void push(IASTName name, IASTNode node) {
if (fDefinitionName != null) { assert !fStack.isEmpty();
fStack.add(new Definition(fDefinitionName, fDefinitionNode)); Definition def = new Definition(name, node);
Definition parent = fStack.get(fStack.size() - 1);
if (parent.fChildren == null) {
parent.fChildren = new ArrayList<>();
} }
parent.fChildren.add(def);
fStack.add(def);
name = getLastInQualified(name); name = getLastInQualified(name);
fDefinitionName= name; fDefinitionName= name;
fDefinitionNode= node; fDefinitionNode= node;
@ -94,12 +139,14 @@ abstract public class IndexerASTVisitor extends ASTVisitor {
private void pop(IASTNode node) { private void pop(IASTNode node) {
if (node == fDefinitionNode) { if (node == fDefinitionNode) {
assert !fStack.isEmpty();
fStack.remove(fStack.size() - 1);
if (fStack.isEmpty()) { if (fStack.isEmpty()) {
fDefinitionName= null; fDefinitionName= null;
fDefinitionNode= null; fDefinitionNode= null;
} }
else { else {
Definition old= fStack.remove(fStack.size()-1); Definition old= fStack.get(fStack.size()-1);
fDefinitionName= old.fName; fDefinitionName= old.fName;
fDefinitionNode= old.fNode; fDefinitionNode= old.fNode;
} }
@ -200,6 +247,11 @@ abstract public class IndexerASTVisitor extends ASTVisitor {
return PROCESS_CONTINUE; return PROCESS_CONTINUE;
} }
public Definition getDefinitionTree() {
assert !fStack.isEmpty();
return fStack.get(0);
}
private int visit(final ICPPASTLambdaExpression lambdaExpr) { private int visit(final ICPPASTLambdaExpression lambdaExpr) {
// Captures // Captures
for (ICPPASTCapture cap : lambdaExpr.getCaptures()) { for (ICPPASTCapture cap : lambdaExpr.getCaptures()) {

View file

@ -20,7 +20,10 @@ import org.eclipse.cdt.ui.testplugin.EditorTestHelper;
import org.eclipse.cdt.internal.ui.editor.CEditor; import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.internal.ui.search.CSearchQuery; import org.eclipse.cdt.internal.ui.search.CSearchQuery;
import org.eclipse.cdt.internal.ui.search.CSearchResult;
import org.eclipse.cdt.internal.ui.search.CSearchTextSelectionQuery; import org.eclipse.cdt.internal.ui.search.CSearchTextSelectionQuery;
import org.eclipse.cdt.internal.ui.search.LineSearchElement;
import org.eclipse.cdt.internal.ui.search.LineSearchElement.Match;
import junit.framework.TestSuite; import junit.framework.TestSuite;
@ -70,4 +73,26 @@ public class FindReferencesTest extends SearchTestBase {
CSearchQuery query = makeProjectQuery(offset, 5); CSearchQuery query = makeProjectQuery(offset, 5);
assertOccurrences(query, 1); assertOccurrences(query, 1);
} }
// #define waldo()
//
// struct S {
// void foo() {
// waldo();
// }
// };
// // empty file
public void testEnclosingDefinitionOfMacroReference_508216() throws Exception {
int offset = fHeaderContents.indexOf("define waldo") + 7;
CSearchQuery query = makeProjectQuery(offset, 5);
CSearchResult result = getSearchResult(query);
Object[] elements = result.getElements();
assertEquals(1, elements.length);
assertInstance(elements[0], LineSearchElement.class);
Match[] matches = ((LineSearchElement) elements[0]).getMatches();
assertEquals(1, matches.length);
assertNotNull(matches[0].getEnclosingElement());
}
} }

View file

@ -56,9 +56,13 @@ public abstract class SearchTestBase extends BaseUITestCase {
super.tearDown(); super.tearDown();
} }
protected void assertOccurrences(CSearchQuery query, int expected) { protected CSearchResult getSearchResult(CSearchQuery query) {
query.run(npm()); query.run(npm());
CSearchResult result= (CSearchResult) query.getSearchResult(); return (CSearchResult) query.getSearchResult();
}
protected void assertOccurrences(CSearchQuery query, int expected) {
CSearchResult result= getSearchResult(query);
assertEquals(expected, result.getMatchCount()); assertEquals(expected, result.getMatchCount());
} }
} }