1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Bug 402498 - Apply declaredBefore() filtering to index bindings

Change-Id: Ic0abe31c67c88fa6f17eed3a231ec9231cd93cb0
This commit is contained in:
Nathan Ridge 2017-01-08 00:01:49 -05:00
parent c3504af925
commit 8c74efdff4
10 changed files with 238 additions and 103 deletions

View file

@ -9062,6 +9062,29 @@ public class AST2TemplateTests extends AST2TestBase {
assertFalse(x.getType().isSameType(CommonCPPTypes.int_));
}
// struct Cat { void meow(); };
// struct Dog { void woof(); };
//
// template <typename T>
// Dog bar(T);
//
// template <typename T>
// auto foo(T t) -> decltype(bar(t));
//
// namespace N {
// class A {};
// }
//
// Cat bar(N::A);
//
// int main() {
// auto x = foo(N::A());
// x.woof();
// }
public void testUnqualifiedFunctionCallInTemplate_402498d() throws Exception {
parseAndCheckBindings();
}
// void bar();
//
// template <typename T>

View file

@ -3035,4 +3035,37 @@ public class IndexCPPTemplateResolutionTest extends IndexBindingResolutionTestBa
public void testBracedInitList_490475() throws Exception {
checkBindings();
}
// struct Cat { void meow(); };
// struct Dog { void woof(); };
// template <typename T>
// Dog bar(T);
//
// template <typename T>
// auto foo(T t) -> decltype(bar(t));
//
// Cat bar(int);
//
// int main() {
// auto x = foo(0);
// x.woof();
// }
public void testUnqualifiedFunctionCallInTemplate_402498() throws Exception {
checkBindings();
}
// template<typename T> struct traits;
// template <typename> struct M;
//
// template<typename T>
// struct traits<M<T>> {
// typedef T type;
// };
//
// typedef traits<M<int>>::type waldo; // ERROR
public void testRegression_402498() throws Exception {
checkBindings();
}
}

View file

@ -280,6 +280,17 @@ public class CPPSemantics {
// "a" => { null, "a", null }
// ":: i" => { "::", "i", null }
private static final Pattern QUALNAME_REGEX = Pattern.compile("^\\s*(::)?\\s*([^\\s:]+)\\s*(?:::(.*))?$"); //$NON-NLS-1$
// This flag controls whether name lookup is allowed to find bindings in headers
// that are not reachable via includes from the file containing the name.
// Generally this is not allowed, but certain consumers, such as IncludeOrganizer,
// need it (since the whole point of IncludeOrganizer is to find missing headers).
private static final ThreadLocal<Boolean> fAllowPromiscuousBindingResolution = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return false;
}
};
static protected IBinding resolveBinding(IASTName name) {
if (traceBindingResolution) {
@ -1931,14 +1942,15 @@ public class CPPSemantics {
if (node == null)
return true;
final int pointOfRef= ((ASTNode) node).getOffset();
// The pointOfRef and pointOfDecl variables contain node offsets scaled by a factor of two.
// This is done to distinguish between left and right points for the same offset.
final int pointOfRef= ((ASTNode) node).getOffset() * 2;
ASTNode nd = null;
if (obj instanceof ICPPSpecialization) {
obj = ((ICPPSpecialization) obj).getSpecializedBinding();
}
int pointOfDecl= -1;
boolean pointOfDeclIsEndOffset = false;
if (obj instanceof ICPPInternalBinding) {
ICPPInternalBinding cpp = (ICPPInternalBinding) obj;
IASTNode[] n = cpp.getDeclarations();
@ -1962,72 +1974,84 @@ public class CPPSemantics {
if (obj instanceof ASTNode) {
nd = (ASTNode) obj;
} else if (obj instanceof ICPPUsingDirective) {
pointOfDecl= ((ICPPUsingDirective) obj).getPointOfDeclaration();
pointOfDecl= ((ICPPUsingDirective) obj).getPointOfDeclaration() * 2;
}
}
if (pointOfDecl < 0 && nd != null) {
ASTNodeProperty prop = nd.getPropertyInParent();
if (prop == IASTDeclarator.DECLARATOR_NAME || nd instanceof IASTDeclarator) {
// Point of declaration for a name is immediately after its complete declarator
// and before its initializer.
IASTDeclarator dtor = (IASTDeclarator)((nd instanceof IASTDeclarator) ? nd : nd.getParent());
while (dtor.getParent() instanceof IASTDeclarator) {
dtor = (IASTDeclarator) dtor.getParent();
if (pointOfDecl < 0) {
if (nd != null) {
pointOfDecl = getPointOfDeclaration(nd);
} else if (obj instanceof IIndexBinding && !isUsingPromiscuousBindingResolution()) {
IIndexBinding indexBinding = (IIndexBinding) obj;
if (indexBinding instanceof ICPPMethod && ((ICPPMethod) indexBinding).isImplicit()) {
return true;
}
IASTInitializer init = dtor.getInitializer();
// [basic.scope.pdecl]/p9: The point of declaration for a template parameter
// is immediately after its complete template-parameter.
// Note: can't just check "dtor.getParent() instanceof ICPPASTTemplateParameter"
// because function parameter declarations implement ICPPASTTemplateParameter too.
boolean isTemplateParameter = dtor.getParent() instanceof ICPPASTTemplateParameter
&& dtor.getParent().getPropertyInParent() == ICPPASTTemplateDeclaration.PARAMETER;
if (init != null && !isTemplateParameter) {
pointOfDecl = ((ASTNode) init).getOffset() - 1;
} else {
pointOfDecl = ((ASTNode) dtor).getOffset() + ((ASTNode) dtor).getLength();
pointOfDeclIsEndOffset = true;
}
} else if (prop == IASTEnumerator.ENUMERATOR_NAME) {
// Point of declaration for an enumerator is immediately after it
// enumerator-definition
IASTEnumerator enumtor = (IASTEnumerator) nd.getParent();
if (enumtor.getValue() != null) {
ASTNode exp = (ASTNode) enumtor.getValue();
pointOfDecl = exp.getOffset() + exp.getLength();
} else {
pointOfDecl = nd.getOffset() + nd.getLength();
}
pointOfDeclIsEndOffset = true;
} else if (prop == ICPPASTUsingDeclaration.NAME) {
nd = (ASTNode) nd.getParent();
pointOfDecl = nd.getOffset();
} else if (prop == ICPPASTNamespaceAlias.ALIAS_NAME) {
nd = (ASTNode) nd.getParent();
pointOfDecl = nd.getOffset() + nd.getLength();
pointOfDeclIsEndOffset = true;
} else if (prop == ICPPASTAliasDeclaration.ALIAS_NAME) {
// [basic.scope.pdecl]/p3: The point of declaration of an alias or alias template
// immediately follows the type-id to which the alias refers.
ASTNode targetType = (ASTNode) ((ICPPASTAliasDeclaration) nd.getParent()).getMappingTypeId();
pointOfDecl = targetType.getOffset() + targetType.getLength();
pointOfDeclIsEndOffset = true;
} else if (prop == ICPPASTSimpleTypeTemplateParameter.PARAMETER_NAME
|| prop == ICPPASTTemplatedTypeTemplateParameter.PARAMETER_NAME) {
// [basic.scope.pdecl]/p9: The point of declaration for a template parameter
// is immediately after its complete template-parameter.
// Type and template template parameters are handled here;
// non-type template parameters are handled in the DECLARATOR_NAME
// case above.
nd = (ASTNode) nd.getParent();
pointOfDecl = nd.getOffset() + nd.getLength();
pointOfDeclIsEndOffset = true;
} else {
pointOfDecl = nd.getOffset() + nd.getLength();
pointOfDeclIsEndOffset = true;
IASTTranslationUnit tu = node.getTranslationUnit();
IIndexFileSet indexFileSet = tu.getIndexFileSet();
return (indexFileSet != null && indexFileSet.containsDeclaration(indexBinding));
}
}
return pointOfDecl < pointOfRef || (pointOfDecl == pointOfRef && pointOfDeclIsEndOffset);
return pointOfDecl < pointOfRef;
}
/**
* Returns the point of declaration for the given AST node. The point of declaration is a node offset
* scaled by a factor of two. This is done to distinguish between left and right points for the offset.
*/
private static int getPointOfDeclaration(ASTNode nd) {
ASTNodeProperty prop = nd.getPropertyInParent();
if (prop == IASTDeclarator.DECLARATOR_NAME || nd instanceof IASTDeclarator) {
// Point of declaration for a name is immediately after its complete declarator
// and before its initializer.
IASTDeclarator dtor = (IASTDeclarator)((nd instanceof IASTDeclarator) ? nd : nd.getParent());
while (dtor.getParent() instanceof IASTDeclarator) {
dtor = (IASTDeclarator) dtor.getParent();
}
IASTInitializer init = dtor.getInitializer();
// [basic.scope.pdecl]/p9: The point of declaration for a template parameter
// is immediately after its complete template-parameter.
// Note: can't just check "dtor.getParent() instanceof ICPPASTTemplateParameter"
// because function parameter declarations implement ICPPASTTemplateParameter too.
boolean isTemplateParameter = dtor.getParent() instanceof ICPPASTTemplateParameter
&& dtor.getParent().getPropertyInParent() == ICPPASTTemplateDeclaration.PARAMETER;
if (init != null && !isTemplateParameter) {
return ((ASTNode) init).getOffset() * 2 - 1;
} else {
return (((ASTNode) dtor).getOffset() + ((ASTNode) dtor).getLength()) * 2 - 1;
}
} else if (prop == IASTEnumerator.ENUMERATOR_NAME) {
// Point of declaration for an enumerator is immediately after it
// enumerator-definition
IASTEnumerator enumtor = (IASTEnumerator) nd.getParent();
if (enumtor.getValue() != null) {
ASTNode exp = (ASTNode) enumtor.getValue();
return (exp.getOffset() + exp.getLength()) * 2 - 1;
} else {
return (nd.getOffset() + nd.getLength()) * 2 - 1;
}
} else if (prop == ICPPASTUsingDeclaration.NAME) {
nd = (ASTNode) nd.getParent();
return nd.getOffset() * 2;
} else if (prop == ICPPASTNamespaceAlias.ALIAS_NAME) {
nd = (ASTNode) nd.getParent();
return (nd.getOffset() + nd.getLength()) * 2 - 1;
} else if (prop == ICPPASTAliasDeclaration.ALIAS_NAME) {
// [basic.scope.pdecl]/p3: The point of declaration of an alias or alias template
// immediately follows the type-id to which the alias refers.
ASTNode targetType = (ASTNode) ((ICPPASTAliasDeclaration) nd.getParent()).getMappingTypeId();
return (targetType.getOffset() + targetType.getLength()) * 2 - 1;
} else if (prop == ICPPASTSimpleTypeTemplateParameter.PARAMETER_NAME
|| prop == ICPPASTTemplatedTypeTemplateParameter.PARAMETER_NAME) {
// [basic.scope.pdecl]/p9: The point of declaration for a template parameter
// is immediately after its complete template-parameter.
// Type and template template parameters are handled here;
// non-type template parameters are handled in the DECLARATOR_NAME
// case above.
nd = (ASTNode) nd.getParent();
return (nd.getOffset() + nd.getLength()) * 2 - 1;
} else {
return (nd.getOffset() + nd.getLength()) * 2 - 1;
}
}
private static boolean acceptDeclaredAfter(ICPPInternalBinding cpp) {
@ -4232,4 +4256,16 @@ public class CPPSemantics {
return binding;
}
public static void enablePromiscuousBindingResolution() {
fAllowPromiscuousBindingResolution.set(true);
}
public static void disablePromiscuousBindingResolution() {
fAllowPromiscuousBindingResolution.set(false);
}
public static boolean isUsingPromiscuousBindingResolution() {
return fAllowPromiscuousBindingResolution.get();
}
}

View file

@ -279,6 +279,7 @@ public class BasicSearchTest extends SearchTestBase {
// void foo() {}
// #include "header.h"
// void bar() {
// foo();
// }
@ -297,6 +298,7 @@ public class BasicSearchTest extends SearchTestBase {
// void foo() {}
// #include "header.h"
// void bar() {foo();foo();foo();}
public void testNewResultsOnSearchAgainB() throws Exception {
CSearchQuery query= makeProjectQuery("foo");
@ -304,7 +306,7 @@ public class BasicSearchTest extends SearchTestBase {
assertOccurrences(query, 4);
// whitespace s.t. new match offset is same as older
String newContent= "void bar() { foo(); }";
String newContent= "#include \"header.h\"\nvoid bar() { foo(); }";
IFile file = fCProject.getProject().getFile(new Path("references.cpp"));
file.setContents(new ByteArrayInputStream(newContent.getBytes()), IResource.FORCE, npm());
runEventQueue(1000);
@ -314,7 +316,7 @@ public class BasicSearchTest extends SearchTestBase {
assertOccurrences(query, 2);
String newContent2= "void bar() {foo(); foo();}";
String newContent2= "#include \"header.h\"\nvoid bar() {foo(); foo();}";
file.setContents(new ByteArrayInputStream(newContent2.getBytes()), IResource.FORCE, npm());
waitForIndexer(fCProject);
@ -326,6 +328,7 @@ public class BasicSearchTest extends SearchTestBase {
// template<typename T> void f(T) {};
// template<typename T> void f(T*) {};
// #include "header.h"
// void a() {
// CT<int>* r1;
// CT<char>* r2;

View file

@ -184,6 +184,7 @@ public class CPPSelectionTestsIndexer extends BaseSelectionTestsIndexer {
// public: void assign(const T* s) {}
// };
// #include "testTemplateClassMethod.h"
// void main() {
// C<char> a;
// a.assign("aaa");
@ -1175,6 +1176,7 @@ public class CPPSelectionTestsIndexer extends BaseSelectionTestsIndexer {
// T operator+(int);
// };
// #include "test.h"
// void main() {
// C<char> a;
// a + 2;

View file

@ -13,8 +13,10 @@ package org.eclipse.cdt.internal.ui.editor;
import java.util.Collection;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
@ -38,10 +40,10 @@ import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.SharedASTJob;
import org.eclipse.cdt.internal.core.model.ASTCache;
import org.eclipse.cdt.internal.ui.BusyCursorJobRunner;
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
@ -100,25 +102,32 @@ public class AddIncludeAction extends TextEditorAction {
}
final MultiTextEdit[] holder = new MultiTextEdit[1];
SharedASTJob job = new SharedASTJob(CEditorMessages.AddInclude_action, tu) {
// We can't use SharedASTJob because IncludeCreator needs to disable promiscuous
// binding resolution, and you can't mix promiscuous and non-promiscuous binding
// resolution in the same AST.
Job job = new Job(CEditorMessages.AddInclude_action) {
@Override
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
if (ast == null) {
return CUIPlugin.createErrorStatus(
NLS.bind(CEditorMessages.AddInclude_ast_not_available, tu.getPath().toOSString()));
}
IIndex index= CCorePlugin.getIndexManager().getIndex(tu.getCProject(),
IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT);
public IStatus run(IProgressMonitor monitor) {
try {
index.acquireReadLock();
IncludeCreator creator = new IncludeCreator(tu, index, fAmbiguityResolver);
holder[0] = creator.createInclude(ast, (ITextSelection) selection);
return Status.OK_STATUS;
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} finally {
index.releaseReadLock();
IIndex index= CCorePlugin.getIndexManager().getIndex(tu.getCProject(),
IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT);
try {
index.acquireReadLock();
IASTTranslationUnit ast = tu.getAST(index, ASTCache.PARSE_MODE);
if (ast == null) {
return CUIPlugin.createErrorStatus(
NLS.bind(CEditorMessages.AddInclude_ast_not_available, tu.getPath().toOSString()));
}
IncludeCreator creator = new IncludeCreator(tu, index, fAmbiguityResolver);
holder[0] = creator.createInclude(ast, (ITextSelection) selection);
return Status.OK_STATUS;
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} finally {
index.releaseReadLock();
}
} catch (CoreException e) {
return e.getStatus();
}
}
};

View file

@ -12,8 +12,10 @@
package org.eclipse.cdt.internal.ui.editor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
@ -30,10 +32,10 @@ import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.SharedASTJob;
import org.eclipse.cdt.internal.core.model.ASTCache;
import org.eclipse.cdt.internal.ui.BusyCursorJobRunner;
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
@ -67,25 +69,32 @@ public class OrganizeIncludesAction extends TextEditorAction {
final IHeaderChooser headerChooser = new InteractiveHeaderChooser(
CEditorMessages.OrganizeIncludes_label, editor.getSite().getShell());
final MultiTextEdit[] holder = new MultiTextEdit[1];
SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) {
// We can't use SharedASTJob because IncludeOrganizer needs to disable promiscuous
// binding resolution, and you can't mix promiscuous and non-promiscuous binding
// resolution in the same AST.
Job job = new Job(CEditorMessages.OrganizeIncludes_action) {
@Override
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException {
if (ast == null) {
return CUIPlugin.createErrorStatus(
NLS.bind(CEditorMessages.OrganizeIncludes_ast_not_available, tu.getPath().toOSString()));
}
IIndex index= CCorePlugin.getIndexManager().getIndex(tu.getCProject(),
IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT);
public IStatus run(IProgressMonitor monitor) {
try {
index.acquireReadLock();
IncludeOrganizer organizer = new IncludeOrganizer(tu, index, headerChooser);
holder[0] = organizer.organizeIncludes(ast);
return Status.OK_STATUS;
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} finally {
index.releaseReadLock();
IIndex index= CCorePlugin.getIndexManager().getIndex(tu.getCProject(),
IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT);
try {
index.acquireReadLock();
IASTTranslationUnit ast = tu.getAST(index, ASTCache.PARSE_MODE);
if (ast == null) {
return CUIPlugin.createErrorStatus(
NLS.bind(CEditorMessages.OrganizeIncludes_ast_not_available, tu.getPath().toOSString()));
}
IncludeOrganizer organizer = new IncludeOrganizer(tu, index, headerChooser);
holder[0] = organizer.organizeIncludes(ast);
return Status.OK_STATUS;
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} finally {
index.releaseReadLock();
}
} catch (CoreException e) {
return e.getStatus();
}
}
};

View file

@ -977,7 +977,15 @@ public class BindingClassifier {
if (fAst == null) {
fAst = node.getTranslationUnit();
}
node.accept(fBindingCollector);
try {
// Enable promiscuous binding resolution for this AST traversal,
// to allow names to be resolved even if the declarations of their
// target bindings are in a header not reachable via includes.
CPPSemantics.enablePromiscuousBindingResolution();
node.accept(fBindingCollector);
} finally {
CPPSemantics.disablePromiscuousBindingResolution();
}
}
/**

View file

@ -77,6 +77,7 @@ import org.eclipse.cdt.ui.IRequiredInclude;
import org.eclipse.cdt.ui.text.ICHelpInvocationContext;
import org.eclipse.cdt.internal.core.dom.parser.IASTInternalScope;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
@ -106,6 +107,16 @@ public class IncludeCreator {
public MultiTextEdit createInclude(IASTTranslationUnit ast, ITextSelection selection)
throws CoreException {
try {
CPPSemantics.enablePromiscuousBindingResolution();
return createIncludeImpl(ast, selection);
} finally {
CPPSemantics.disablePromiscuousBindingResolution();
}
}
private MultiTextEdit createIncludeImpl(IASTTranslationUnit ast, ITextSelection selection)
throws CoreException {
MultiTextEdit rootEdit = new MultiTextEdit();
ITranslationUnit tu = fContext.getTranslationUnit();
IASTNodeSelector selector = ast.getNodeSelector(tu.getLocation().toOSString());

View file

@ -92,6 +92,7 @@ public class QmlRegistrationTests extends BaseQtTestCase {
}
}
// #include "junit-QObject.hh"
// class T;
//
// static void func()