mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-23 14:42:11 +02:00
Bug 345872 - Show correct override markers in the presence of multiple
iheritance Change-Id: I6d9196d06d2077208ba246991ec712897c09a0df Signed-off-by: Patrick Hofer <paedu.hofer@gmail.com> Signed-off-by: Nathan Ridge <zeratul976@hotmail.com>
This commit is contained in:
parent
4dd74cb463
commit
bb4b74b367
9 changed files with 965 additions and 377 deletions
|
@ -0,0 +1,176 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2015 Patrick Hofer
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Patrick Hofer - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.ui.tests;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.eclipse.core.resources.IFile;
|
||||||
|
import org.eclipse.core.runtime.CoreException;
|
||||||
|
import org.eclipse.jface.text.BadLocationException;
|
||||||
|
import org.eclipse.jface.text.IDocument;
|
||||||
|
import org.eclipse.jface.text.ISynchronizable;
|
||||||
|
import org.eclipse.jface.text.Position;
|
||||||
|
import org.eclipse.jface.text.source.Annotation;
|
||||||
|
import org.eclipse.jface.text.source.IAnnotationModel;
|
||||||
|
import org.eclipse.ui.PartInitException;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.core.parser.util.ArrayUtil;
|
||||||
|
import org.eclipse.cdt.ui.testplugin.EditorTestHelper;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.internal.ui.editor.CEditor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for tests. If you want to use outside of this plugin, you need
|
||||||
|
* to override {@link #getPlugin()} method and maybe {@link #getSourcePrefix()}
|
||||||
|
* method to get source directory for the tests,
|
||||||
|
* default is "src". To make it read comment from java class, you need to
|
||||||
|
* include this source directory (with test java files) into the build bundle.
|
||||||
|
*/
|
||||||
|
public class AnnotationTestCase extends UITestCaseWithProject {
|
||||||
|
private IAnnotationModel fAnnotationModel;
|
||||||
|
private Object fAnnotationModelLockObject;
|
||||||
|
protected Annotation[] annotations;
|
||||||
|
|
||||||
|
private CEditor fEditor;
|
||||||
|
|
||||||
|
protected String testedAnnotationId = Annotation.TYPE_UNKNOWN;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkAnnotationType(Annotation a) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Annotation checkAnnotationLine(int i) {
|
||||||
|
return checkAnnotationLine(currentFile, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkAnnotationLines(Object... args) {
|
||||||
|
for (Object i : args) {
|
||||||
|
checkAnnotationLine((Integer) i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Annotation checkAnnotationLine(int i, String annotationId) {
|
||||||
|
return checkAnnotationLine(currentFile, i, annotationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Annotation checkAnnotationLine(File file, int expectedLine) {
|
||||||
|
return checkAnnotationLine(file, expectedLine, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Annotation checkAnnotationLine(File file, int expectedLine, String annotationId) {
|
||||||
|
assertTrue(annotations != null);
|
||||||
|
assertTrue("No annotations found but should", annotations.length > 0); //$NON-NLS-1$
|
||||||
|
int line = 0;
|
||||||
|
Annotation a = null;
|
||||||
|
for (Annotation annotation : annotations) {
|
||||||
|
line = getLine(annotation);
|
||||||
|
if (line == expectedLine && (annotationId == null || annotationId.equals(annotation.getType()))) {
|
||||||
|
a = annotation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertNotNull(a);
|
||||||
|
if (a != null) {
|
||||||
|
assertEquals(expectedLine, line);
|
||||||
|
assertTrue(checkAnnotationType(a));
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getLine(Annotation annotation) {
|
||||||
|
int line = 0;
|
||||||
|
IDocument document = fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput());
|
||||||
|
Position position = fAnnotationModel.getPosition(annotation);
|
||||||
|
try {
|
||||||
|
line = document.getLineOfOffset(position.getOffset()) + 1;
|
||||||
|
} catch (BadLocationException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkNoAnnotations() {
|
||||||
|
if (annotations == null || annotations.length == 0) {
|
||||||
|
// all good
|
||||||
|
} else {
|
||||||
|
Annotation m = annotations[0];
|
||||||
|
fail("Found " + annotations.length + " annotation but should not.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runOnProject() {
|
||||||
|
try {
|
||||||
|
indexFiles();
|
||||||
|
} catch (CoreException | InterruptedException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
runInEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadCodeAndRun(String code) {
|
||||||
|
loadCode(code);
|
||||||
|
runInEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadCodeAndRunCpp(String code) {
|
||||||
|
loadCode(code, true);
|
||||||
|
runInEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void runInEditor() {
|
||||||
|
try {
|
||||||
|
annotations = null;
|
||||||
|
fEditor= openCEditor(currentIFile);
|
||||||
|
assertNotNull(fEditor);
|
||||||
|
EditorTestHelper.joinReconciler(EditorTestHelper.getSourceViewer(fEditor), 100, 1000000, 1000);
|
||||||
|
|
||||||
|
fAnnotationModel= fEditor.getDocumentProvider().getAnnotationModel(fEditor.getEditorInput());
|
||||||
|
fAnnotationModelLockObject = getLockObject(fAnnotationModel);
|
||||||
|
synchronized (fAnnotationModelLockObject) {
|
||||||
|
for (Iterator<Annotation> iter= fAnnotationModel.getAnnotationIterator(); iter.hasNext();) {
|
||||||
|
Annotation anotation= iter.next();
|
||||||
|
if (anotation != null && (testedAnnotationId.equals(anotation.getType()))) {
|
||||||
|
annotations = ArrayUtil.append(Annotation.class, annotations, anotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
annotations = ArrayUtil.trim(Annotation.class, annotations);
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getLockObject(IAnnotationModel annotationModel) {
|
||||||
|
if (annotationModel instanceof ISynchronizable) {
|
||||||
|
Object lock = ((ISynchronizable) annotationModel).getLockObject();
|
||||||
|
if (lock != null)
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
return annotationModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CEditor openCEditor(IFile file) {
|
||||||
|
assertNotNull(file);
|
||||||
|
assertTrue(file.exists());
|
||||||
|
try {
|
||||||
|
return (CEditor) EditorTestHelper.openInEditor(file, true);
|
||||||
|
} catch (PartInitException e) {
|
||||||
|
fail();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,7 +53,6 @@ import org.eclipse.cdt.ui.testplugin.CTestPlugin;
|
||||||
import org.eclipse.cdt.ui.testplugin.util.StringAsserts;
|
import org.eclipse.cdt.ui.testplugin.util.StringAsserts;
|
||||||
|
|
||||||
public class BaseUITestCase extends BaseTestCase {
|
public class BaseUITestCase extends BaseTestCase {
|
||||||
|
|
||||||
public BaseUITestCase() {
|
public BaseUITestCase() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -65,6 +64,7 @@ public class BaseUITestCase extends BaseTestCase {
|
||||||
@Override
|
@Override
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
|
|
||||||
final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
|
final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
|
||||||
IViewPart view= activePage.findView("org.eclipse.cdt.ui.tests.DOMAST.DOMAST");
|
IViewPart view= activePage.findView("org.eclipse.cdt.ui.tests.DOMAST.DOMAST");
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
|
@ -111,8 +111,9 @@ public class BaseUITestCase extends BaseTestCase {
|
||||||
protected void runEventQueue(int time) {
|
protected void runEventQueue(int time) {
|
||||||
final long endTime= System.currentTimeMillis() + time;
|
final long endTime= System.currentTimeMillis() + time;
|
||||||
while (true) {
|
while (true) {
|
||||||
while (Display.getCurrent().readAndDispatch())
|
while (Display.getCurrent().readAndDispatch()) {
|
||||||
;
|
}
|
||||||
|
|
||||||
long diff= endTime - System.currentTimeMillis();
|
long diff= endTime - System.currentTimeMillis();
|
||||||
if (diff <= 0) {
|
if (diff <= 0) {
|
||||||
break;
|
break;
|
||||||
|
@ -216,7 +217,7 @@ public class BaseUITestCase extends BaseTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void executeCommand(IViewPart viewPart, String commandID) throws ExecutionException, NotDefinedException, NotEnabledException, NotHandledException {
|
protected void executeCommand(IViewPart viewPart, String commandID) throws ExecutionException, NotDefinedException, NotEnabledException, NotHandledException {
|
||||||
IHandlerService hs= (IHandlerService)viewPart.getSite().getService(IHandlerService.class);
|
IHandlerService hs= viewPart.getSite().getService(IHandlerService.class);
|
||||||
assertNotNull(hs);
|
assertNotNull(hs);
|
||||||
hs.executeCommand(commandID, null);
|
hs.executeCommand(commandID, null);
|
||||||
}
|
}
|
||||||
|
@ -316,7 +317,7 @@ public class BaseUITestCase extends BaseTestCase {
|
||||||
try {
|
try {
|
||||||
TreeItem firstItem= i0Node.getItem(0);
|
TreeItem firstItem= i0Node.getItem(0);
|
||||||
firstItemText= firstItem.getText();
|
firstItemText= firstItem.getText();
|
||||||
if (firstItemText.length() > 0 && !firstItemText.equals("...")) {
|
if (!firstItemText.isEmpty() && !firstItemText.equals("...")) {
|
||||||
TreeItem item = i0Node.getItem(i1);
|
TreeItem item = i0Node.getItem(i1);
|
||||||
nodePresent = true;
|
nodePresent = true;
|
||||||
if (label != null && label.equals(item.getText())) {
|
if (label != null && label.equals(item.getText())) {
|
||||||
|
@ -355,4 +356,4 @@ public class BaseUITestCase extends BaseTestCase {
|
||||||
// is better than not having that sort of test at all, which some would argue is
|
// is better than not having that sort of test at all, which some would argue is
|
||||||
// the better approach. In practice, it takes about 60-150 ms for the item to
|
// the better approach. In practice, it takes about 60-150 ms for the item to
|
||||||
// appear (on my machine), but we give it up to five seconds. Waiting one second
|
// appear (on my machine), but we give it up to five seconds. Waiting one second
|
||||||
// for it to not appear should be more than adequate
|
// for it to not appear should be more than adequate.
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2015 Patrick Hofer
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Patrick Hofer - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.ui.tests;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static methods for use in tests.
|
||||||
|
*/
|
||||||
|
public class TestUtils {
|
||||||
|
public static File saveFile(InputStream st, File testFile) throws FileNotFoundException, IOException {
|
||||||
|
BufferedReader r = new BufferedReader(new InputStreamReader(st));
|
||||||
|
String line;
|
||||||
|
try (PrintStream wr = new PrintStream(testFile)) {
|
||||||
|
while ((line = r.readLine()) != null) {
|
||||||
|
wr.println(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return testFile;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2015 Patrick Hofer.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Patrick Hofer - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.ui.tests;
|
||||||
|
|
||||||
|
import org.eclipse.core.runtime.Plugin;
|
||||||
|
import org.osgi.framework.BundleContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The activator class controls the plug-in life cycle
|
||||||
|
*/
|
||||||
|
public class UICoreTestActivator extends Plugin {
|
||||||
|
// The plug-in ID
|
||||||
|
public static final String PLUGIN_ID = "org.eclipse.cdt.ui.tests"; //$NON-NLS-1$
|
||||||
|
// The shared instance
|
||||||
|
private static UICoreTestActivator plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constructor
|
||||||
|
*/
|
||||||
|
public UICoreTestActivator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(BundleContext context) throws Exception {
|
||||||
|
super.start(context);
|
||||||
|
plugin = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop(BundleContext context) throws Exception {
|
||||||
|
plugin = null;
|
||||||
|
super.stop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the shared instance
|
||||||
|
*/
|
||||||
|
public static UICoreTestActivator getDefault() {
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,205 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2015 Patrick Hofer and others.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Patrick Hofer - initial API and implementation
|
||||||
|
* Nathan Ridge
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.ui.tests;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.eclipse.core.resources.IFile;
|
||||||
|
import org.eclipse.core.resources.IProject;
|
||||||
|
import org.eclipse.core.resources.IResource;
|
||||||
|
import org.eclipse.core.resources.IWorkspace;
|
||||||
|
import org.eclipse.core.resources.IWorkspaceRunnable;
|
||||||
|
import org.eclipse.core.resources.ResourcesPlugin;
|
||||||
|
import org.eclipse.core.runtime.CoreException;
|
||||||
|
import org.eclipse.core.runtime.IProgressMonitor;
|
||||||
|
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||||
|
import org.eclipse.core.runtime.Path;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.core.CCorePlugin;
|
||||||
|
import org.eclipse.cdt.core.dom.IPDOMManager;
|
||||||
|
import org.eclipse.cdt.core.model.CModelException;
|
||||||
|
import org.eclipse.cdt.core.model.ICElement;
|
||||||
|
import org.eclipse.cdt.core.model.ICProject;
|
||||||
|
import org.eclipse.cdt.core.testplugin.CProjectHelper;
|
||||||
|
import org.eclipse.cdt.core.testplugin.FileManager;
|
||||||
|
|
||||||
|
public class UITestCaseWithProject extends BaseUITestCase {
|
||||||
|
ArrayList<File> tempFiles = new ArrayList<>();
|
||||||
|
protected File tmpDir;
|
||||||
|
protected ICProject cproject;
|
||||||
|
protected File currentFile;
|
||||||
|
protected ICElement currentCElem;
|
||||||
|
protected IFile currentIFile;
|
||||||
|
IProgressMonitor monitor = new NullProgressMonitor();
|
||||||
|
static FileManager fileManager = new FileManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override for c++ (i.e. at least one c++ test)
|
||||||
|
*
|
||||||
|
* @return is c++ tests
|
||||||
|
*/
|
||||||
|
public boolean isCpp() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
removeLeftOverProjects();
|
||||||
|
cproject = createProject(isCpp());
|
||||||
|
tmpDir = cproject.getProject().getLocation().makeAbsolute().toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception {
|
||||||
|
if (cproject != null) {
|
||||||
|
cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, new NullProgressMonitor());
|
||||||
|
}
|
||||||
|
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeLeftOverProjects() throws CoreException {
|
||||||
|
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
|
||||||
|
IProject[] projects = workspace.getRoot().getProjects();
|
||||||
|
for (int i = 0; i < projects.length; i++) {
|
||||||
|
IProject p = projects[i];
|
||||||
|
if (p.getName().startsWith("Codan")) {
|
||||||
|
p.delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, new NullProgressMonitor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ICProject createProject(final boolean cpp) throws CoreException {
|
||||||
|
final ICProject cprojects[] = new ICProject[1];
|
||||||
|
ModelJoiner mj = new ModelJoiner();
|
||||||
|
try {
|
||||||
|
// Create the cproject
|
||||||
|
final String projectName = "CDTUIProjTest_" + System.currentTimeMillis();
|
||||||
|
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
|
||||||
|
workspace.run(new IWorkspaceRunnable() {
|
||||||
|
@Override
|
||||||
|
public void run(IProgressMonitor monitor) throws CoreException {
|
||||||
|
// Create the cproject
|
||||||
|
ICProject cproject = cpp ?
|
||||||
|
CProjectHelper.createCCProject(projectName, null, IPDOMManager.ID_NO_INDEXER) :
|
||||||
|
CProjectHelper.createCProject(projectName, null, IPDOMManager.ID_NO_INDEXER);
|
||||||
|
cprojects[0] = cproject;
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
mj.join();
|
||||||
|
} finally {
|
||||||
|
mj.dispose();
|
||||||
|
}
|
||||||
|
return cprojects[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void indexFiles() throws CoreException, InterruptedException {
|
||||||
|
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
|
||||||
|
workspace.run(new IWorkspaceRunnable() {
|
||||||
|
@Override
|
||||||
|
public void run(IProgressMonitor monitor) throws CoreException {
|
||||||
|
cproject.getProject().refreshLocal(1, monitor);
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
// Index the cproject
|
||||||
|
CCorePlugin.getIndexManager().setIndexerId(cproject, IPDOMManager.ID_FAST_INDEXER);
|
||||||
|
CCorePlugin.getIndexManager().reindex(cproject);
|
||||||
|
waitForIndexer(cproject);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int pos2Line(int pos) throws IOException {
|
||||||
|
FileInputStream st = new FileInputStream(currentFile);
|
||||||
|
try {
|
||||||
|
int c;
|
||||||
|
int line = 1;
|
||||||
|
int cur = 0;
|
||||||
|
while ((c = st.read()) != -1) {
|
||||||
|
if (c == '\n')
|
||||||
|
line++;
|
||||||
|
if (cur >= pos)
|
||||||
|
return line;
|
||||||
|
cur++;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
st.close();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File loadCode(String code, boolean cpp) {
|
||||||
|
String fileKey = "@file:";
|
||||||
|
int indf = code.indexOf(fileKey);
|
||||||
|
if (indf >= 0) {
|
||||||
|
int sep = code.indexOf('\n');
|
||||||
|
if (sep != -1) {
|
||||||
|
String line = code.substring(0, sep);
|
||||||
|
code = code.substring(sep + 1);
|
||||||
|
String fileName = line.substring(indf + fileKey.length()).trim();
|
||||||
|
return loadCode(code, new File(tmpDir, fileName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String ext = cpp ? ".cpp" : ".c";
|
||||||
|
File testFile = null;
|
||||||
|
try {
|
||||||
|
testFile = File.createTempFile("test", ext, tmpDir); //$NON-NLS-1$
|
||||||
|
} catch (IOException e1) {
|
||||||
|
fail(e1.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return loadCode(code, testFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public File loadCode(String code, String filename) {
|
||||||
|
File testFile = new File(tmpDir, filename);
|
||||||
|
return loadCode(code, testFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File loadCode(String code, File testFile) {
|
||||||
|
try {
|
||||||
|
tempFiles.add(testFile);
|
||||||
|
TestUtils.saveFile(new ByteArrayInputStream(code.trim().getBytes()), testFile);
|
||||||
|
currentFile = testFile;
|
||||||
|
try {
|
||||||
|
cproject.getProject().refreshLocal(1, null);
|
||||||
|
} catch (CoreException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
currentCElem = cproject.findElement(new Path(currentFile.toString()));
|
||||||
|
currentIFile = (IFile) currentCElem.getResource();
|
||||||
|
return testFile;
|
||||||
|
} catch (IOException e) {
|
||||||
|
fail("Cannot save test: " + testFile + ": " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
} catch (CModelException e) {
|
||||||
|
fail("Cannot find file: " + testFile + ": " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public File loadCodeC(String code) {
|
||||||
|
return loadCode(code, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public File loadCodeCpp(String code) {
|
||||||
|
return loadCode(code, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public File loadCode(String code) {
|
||||||
|
return loadCode(code, isCpp());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2015 Patrick Hofer and others.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Patrick Hofer - Initial API and implementation
|
||||||
|
* Tomasz Wesolowski - more tests
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.ui.tests.text;
|
||||||
|
|
||||||
|
import org.eclipse.jface.text.source.Annotation;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.ui.tests.AnnotationTestCase;
|
||||||
|
|
||||||
|
import org.eclipse.cdt.internal.ui.editor.OverrideIndicatorManager;
|
||||||
|
import org.eclipse.cdt.internal.ui.editor.OverrideIndicatorManager.OverrideIndicator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for {@see OverrideIndicatorManager} class.
|
||||||
|
*/
|
||||||
|
public class OverrideIndicatorTest extends AnnotationTestCase {
|
||||||
|
private Integer expectedAnnotationType;
|
||||||
|
|
||||||
|
public OverrideIndicatorTest() {
|
||||||
|
super();
|
||||||
|
testedAnnotationId = OverrideIndicator.ANNOTATION_TYPE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
//enableProblems(NonVirtualDestructor.ER_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCpp() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkAnnotationType(Annotation annotation) {
|
||||||
|
if (annotation instanceof OverrideIndicator) {
|
||||||
|
if (expectedAnnotationType != null) {
|
||||||
|
OverrideIndicator oi = (OverrideIndicator) annotation;
|
||||||
|
return expectedAnnotationType == oi.getIndicationType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkImplementsAnnotationLines(Object... args) {
|
||||||
|
expectedAnnotationType = OverrideIndicatorManager.ANNOTATION_IMPLEMENTS;
|
||||||
|
try {
|
||||||
|
checkAnnotationLines(args);
|
||||||
|
} finally {
|
||||||
|
expectedAnnotationType = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkOverridesAnnotationLines(Object... args) {
|
||||||
|
expectedAnnotationType = OverrideIndicatorManager.ANNOTATION_OVERRIDES;
|
||||||
|
try {
|
||||||
|
checkAnnotationLines(args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
expectedAnnotationType = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkShadowsAnnotationLines(Object... args) {
|
||||||
|
expectedAnnotationType = OverrideIndicatorManager.ANNOTATION_SHADOWS;
|
||||||
|
try {
|
||||||
|
checkAnnotationLines(args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
expectedAnnotationType = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// class A {
|
||||||
|
// virtual void vm() = 0;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class B : public A {
|
||||||
|
// virtual void vm() {};
|
||||||
|
// };
|
||||||
|
public void testSimpleImplementedAnnotation() throws Exception{
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
checkImplementsAnnotationLines(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// class A {
|
||||||
|
// virtual void vm() {};
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class B : public A {
|
||||||
|
// virtual void vm() {};
|
||||||
|
// };
|
||||||
|
public void testSimpleOverridenAnnotation() throws Exception{
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
checkOverridesAnnotationLines(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// class A {
|
||||||
|
// void m(void) {};
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class B : public A {
|
||||||
|
// void m(int param) {};
|
||||||
|
// };
|
||||||
|
public void testSimpleShadowedAnnotation() throws Exception{
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
checkShadowsAnnotationLines(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// class A {
|
||||||
|
// virtual void m(void) {};
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class B : public A {
|
||||||
|
// void m(int param) {};
|
||||||
|
// };
|
||||||
|
public void testShadowedVirtualAnnotation1() throws Exception{
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
// Non-virtual shadowing virtual
|
||||||
|
checkShadowsAnnotationLines(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// class A {
|
||||||
|
// void m(void) {};
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class B : public A {
|
||||||
|
// virtual void m(int param) {};
|
||||||
|
// };
|
||||||
|
public void testShadowedVirtualAnnotation2() throws Exception{
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
// Virtual shadowing non-virtual
|
||||||
|
checkShadowsAnnotationLines(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// class A {
|
||||||
|
// virtual void m(void) {};
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class B : public A {
|
||||||
|
// virtual void m(int param) {};
|
||||||
|
// };
|
||||||
|
public void testShadowedVirtualAnnotation3() throws Exception{
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
// Virtual shadowing virtual
|
||||||
|
checkShadowsAnnotationLines(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct X {
|
||||||
|
// virtual ~X();
|
||||||
|
// virtual void foo() const;
|
||||||
|
// };
|
||||||
|
// struct Y : X {
|
||||||
|
// void foo();
|
||||||
|
// };
|
||||||
|
public void testShadowedConstByNonConst() throws Exception {
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
// CV-qualifiers produce different overloads
|
||||||
|
checkShadowsAnnotationLines(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct X {
|
||||||
|
// virtual ~X();
|
||||||
|
// virtual void foo();
|
||||||
|
// };
|
||||||
|
// struct Y : X {
|
||||||
|
// virtual void foo() volatile;
|
||||||
|
// };
|
||||||
|
public void testShadowedNonVolatileByVolatile() throws Exception {
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
// CV-qualifiers produce different overloads
|
||||||
|
checkShadowsAnnotationLines(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// class I1 {
|
||||||
|
// virtual void vm1(void) = 0;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class I2 {
|
||||||
|
// virtual void vm2(void) = 0;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class D : I1, I2 {
|
||||||
|
// public:
|
||||||
|
// virtual void vm1(void) {};
|
||||||
|
// virtual void vm2(void) {};
|
||||||
|
// };
|
||||||
|
public void testAnnotationsWithMultipleInheritance() throws Exception{
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
checkImplementsAnnotationLines(11, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
// class I1 {
|
||||||
|
// virtual void vm1(void) = 0;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class I2 {
|
||||||
|
// virtual void vm2(void) = 0;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class D : I2, I1 {
|
||||||
|
// public:
|
||||||
|
// virtual void vm1(void) {};
|
||||||
|
// virtual void vm2(void) {};
|
||||||
|
// };
|
||||||
|
public void testAnnotationsWithMultipleInheritanceReverseOrder() throws Exception{
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
checkImplementsAnnotationLines(11, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
// class I1 {
|
||||||
|
// virtual void vm1(void) = 0;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class I2 {
|
||||||
|
// virtual void vm2(void) = 0;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class B1 {
|
||||||
|
// public:
|
||||||
|
// void m1(void) {};
|
||||||
|
// virtual void vm3(void) {};
|
||||||
|
// virtual ~B1();
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class B2 : public B1 {
|
||||||
|
// public:
|
||||||
|
// void m2(void) {};
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// class D : public B2, I1, I2 {
|
||||||
|
// public:
|
||||||
|
// void m1(void) {}; // line 23
|
||||||
|
// void m2(int param) {};
|
||||||
|
// virtual void vm1(void) {};
|
||||||
|
// virtual void vm2(void) {};
|
||||||
|
// virtual void vm3(void) {};
|
||||||
|
// virtual void vm4(void) {};
|
||||||
|
// };
|
||||||
|
public void testAnnotationsInClassHierarchy() throws Exception{
|
||||||
|
loadCodeAndRun(getAboveComment());
|
||||||
|
checkShadowsAnnotationLines(23, 24);
|
||||||
|
checkImplementsAnnotationLines(25, 26);
|
||||||
|
checkOverridesAnnotationLines(27);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2005, 2014 IBM Corporation and others.
|
* Copyright (c) 2005, 2015 IBM Corporation and others.
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
|
@ -374,6 +374,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
|
||||||
|
|
||||||
// This method is called when the Platform version is 4.5 or higher.
|
// This method is called when the Platform version is 4.5 or higher.
|
||||||
// @Override
|
// @Override
|
||||||
|
@Override
|
||||||
protected IFormattingContext createFormattingContext(int selectionOffset, int selectionLength) {
|
protected IFormattingContext createFormattingContext(int selectionOffset, int selectionLength) {
|
||||||
return createFormattingContext(selectionOffset, selectionLength, true);
|
return createFormattingContext(selectionOffset, selectionLength, true);
|
||||||
}
|
}
|
||||||
|
@ -600,8 +601,8 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
|
||||||
private boolean fCloseAngularBrackets = true;
|
private boolean fCloseAngularBrackets = true;
|
||||||
private boolean fCloseBraces = true;
|
private boolean fCloseBraces = true;
|
||||||
private final String CATEGORY = toString();
|
private final String CATEGORY = toString();
|
||||||
private IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY);
|
private final IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY);
|
||||||
private Deque<BracketLevel> fBracketLevelStack = new ArrayDeque<>();
|
private final Deque<BracketLevel> fBracketLevelStack = new ArrayDeque<>();
|
||||||
|
|
||||||
public void setCloseBracketsEnabled(boolean enabled) {
|
public void setCloseBracketsEnabled(boolean enabled) {
|
||||||
fCloseBrackets = enabled;
|
fCloseBrackets = enabled;
|
||||||
|
@ -1275,7 +1276,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
|
||||||
protected CPairMatcher fBracketMatcher = new CPairMatcher(BRACKETS);
|
protected CPairMatcher fBracketMatcher = new CPairMatcher(BRACKETS);
|
||||||
|
|
||||||
/** The bracket inserter. */
|
/** The bracket inserter. */
|
||||||
private BracketInserter fBracketInserter = new BracketInserter();
|
private final BracketInserter fBracketInserter = new BracketInserter();
|
||||||
|
|
||||||
/** Listener to annotation model changes that updates the error tick in the tab image */
|
/** Listener to annotation model changes that updates the error tick in the tab image */
|
||||||
private CEditorErrorTickUpdater fCEditorErrorTickUpdater;
|
private CEditorErrorTickUpdater fCEditorErrorTickUpdater;
|
||||||
|
@ -1320,7 +1321,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
|
||||||
* AST reconciling listeners.
|
* AST reconciling listeners.
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
private ListenerList fReconcilingListeners= new ListenerList(ListenerList.IDENTITY);
|
private final ListenerList fReconcilingListeners= new ListenerList(ListenerList.IDENTITY);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Semantic highlighting manager
|
* Semantic highlighting manager
|
||||||
|
@ -3602,7 +3603,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
|
||||||
if (model == null)
|
if (model == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fOverrideIndicatorManager= new OverrideIndicatorManager(model);
|
fOverrideIndicatorManager= new OverrideIndicatorManager(model, null);
|
||||||
|
|
||||||
addReconcileListener(fOverrideIndicatorManager);
|
addReconcileListener(fOverrideIndicatorManager);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2010 Tomasz Wesolowski and others
|
* Copyright (c) 2010, 2015 Tomasz Wesolowski and others
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
|
@ -19,46 +19,34 @@ import org.eclipse.cdt.internal.ui.CPluginImages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Tomasz Wesolowski
|
* @author Tomasz Wesolowski
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class OverrideIndicatorImageProvider implements
|
public class OverrideIndicatorImageProvider implements IAnnotationImageProvider {
|
||||||
IAnnotationImageProvider {
|
|
||||||
|
|
||||||
private static final String OVERRIDE_IMG_DESC_ID = "CPluginImages.DESC_OBJS_OVERRIDES"; //$NON-NLS-1$
|
private static final String OVERRIDE_IMG_DESC_ID = "CPluginImages.DESC_OBJS_OVERRIDES"; //$NON-NLS-1$
|
||||||
private static final String IMPLEMENT_IMG_DESC_ID = "CPluginImages.DESC_OBJS_IMPLEMENTS"; //$NON-NLS-1$
|
private static final String IMPLEMENT_IMG_DESC_ID = "CPluginImages.DESC_OBJS_IMPLEMENTS"; //$NON-NLS-1$
|
||||||
private static final String SHADOW_IMG_DESC_ID = "CPluginImages.DESC_OBJS_SHADOWS"; //$NON-NLS-1$
|
private static final String SHADOW_IMG_DESC_ID = "CPluginImages.DESC_OBJS_SHADOWS"; //$NON-NLS-1$
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.eclipse.ui.texteditor.IAnnotationImageProvider#getManagedImage(org.eclipse.jface.text.source.Annotation)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Image getManagedImage(Annotation annotation) {
|
public Image getManagedImage(Annotation annotation) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.eclipse.ui.texteditor.IAnnotationImageProvider#getImageDescriptorId(org.eclipse.jface.text.source.Annotation)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getImageDescriptorId(Annotation annotation) {
|
public String getImageDescriptorId(Annotation annotation) {
|
||||||
if (!isImageProviderFor(annotation)) {
|
if (!isImageProviderFor(annotation)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
switch (getAnnotationType(annotation)) {
|
switch (getAnnotationType(annotation)) {
|
||||||
case OverrideIndicatorManager.RESULT_OVERRIDES:
|
case OverrideIndicatorManager.ANNOTATION_OVERRIDES:
|
||||||
return OVERRIDE_IMG_DESC_ID;
|
return OVERRIDE_IMG_DESC_ID;
|
||||||
case OverrideIndicatorManager.RESULT_IMPLEMENTS:
|
case OverrideIndicatorManager.ANNOTATION_IMPLEMENTS:
|
||||||
return IMPLEMENT_IMG_DESC_ID;
|
return IMPLEMENT_IMG_DESC_ID;
|
||||||
case OverrideIndicatorManager.RESULT_SHADOWS:
|
case OverrideIndicatorManager.ANNOTATION_SHADOWS:
|
||||||
return SHADOW_IMG_DESC_ID;
|
return SHADOW_IMG_DESC_ID;
|
||||||
}
|
}
|
||||||
assert false;
|
assert false;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
|
||||||
* @see org.eclipse.ui.texteditor.IAnnotationImageProvider#getImageDescriptor(java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public ImageDescriptor getImageDescriptor(String imageDescritporId) {
|
public ImageDescriptor getImageDescriptor(String imageDescritporId) {
|
||||||
if (imageDescritporId.equals(OVERRIDE_IMG_DESC_ID)) {
|
if (imageDescritporId.equals(OVERRIDE_IMG_DESC_ID)) {
|
||||||
|
@ -79,5 +67,4 @@ public class OverrideIndicatorImageProvider implements
|
||||||
private int getAnnotationType(Annotation annotation) {
|
private int getAnnotationType(Annotation annotation) {
|
||||||
return ((OverrideIndicatorManager.OverrideIndicator)annotation).getIndicationType();
|
return ((OverrideIndicatorManager.OverrideIndicator)annotation).getIndicationType();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2010, 2012 Tomasz Wesolowski and others
|
* Copyright (c) 2010, 2015 Tomasz Wesolowski and others
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
|
@ -8,40 +8,36 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Tomasz Wesolowski - initial API and implementation
|
* Tomasz Wesolowski - initial API and implementation
|
||||||
* Sergey Prigogin (Google)
|
* Sergey Prigogin (Google)
|
||||||
|
* Patrick Hofer [bug 345872]
|
||||||
|
* Nathan Ridge [bug 345872]
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.internal.ui.editor;
|
package org.eclipse.cdt.internal.ui.editor;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashMap;
|
||||||
import java.util.Set;
|
import java.util.Map;
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import org.eclipse.core.runtime.CoreException;
|
import org.eclipse.core.runtime.CoreException;
|
||||||
import org.eclipse.core.runtime.IProgressMonitor;
|
import org.eclipse.core.runtime.IProgressMonitor;
|
||||||
|
import org.eclipse.core.runtime.NullProgressMonitor;
|
||||||
import org.eclipse.jface.text.ISynchronizable;
|
import org.eclipse.jface.text.ISynchronizable;
|
||||||
import org.eclipse.jface.text.Position;
|
import org.eclipse.jface.text.Position;
|
||||||
import org.eclipse.jface.text.source.Annotation;
|
import org.eclipse.jface.text.source.Annotation;
|
||||||
import org.eclipse.jface.text.source.IAnnotationModel;
|
import org.eclipse.jface.text.source.IAnnotationModel;
|
||||||
|
import org.eclipse.jface.text.source.IAnnotationModelExtension;
|
||||||
|
|
||||||
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
|
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
|
||||||
import org.eclipse.cdt.core.dom.ast.DOMException;
|
import org.eclipse.cdt.core.dom.ast.DOMException;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
|
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
|
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
|
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
|
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
|
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
|
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
|
|
||||||
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.IType;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
|
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding;
|
|
||||||
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.ICPPFunctionType;
|
|
||||||
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
|
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
|
||||||
import org.eclipse.cdt.core.index.IIndex;
|
import org.eclipse.cdt.core.index.IIndex;
|
||||||
|
import org.eclipse.cdt.core.parser.util.ArrayUtil;
|
||||||
import org.eclipse.cdt.ui.CDTUITools;
|
import org.eclipse.cdt.ui.CDTUITools;
|
||||||
|
|
||||||
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
|
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
|
||||||
|
@ -53,47 +49,22 @@ import org.eclipse.cdt.internal.ui.text.ICReconcilingListener;
|
||||||
import org.eclipse.cdt.internal.ui.viewsupport.IndexUI;
|
import org.eclipse.cdt.internal.ui.viewsupport.IndexUI;
|
||||||
|
|
||||||
public class OverrideIndicatorManager implements ICReconcilingListener {
|
public class OverrideIndicatorManager implements ICReconcilingListener {
|
||||||
static final String ANNOTATION_TYPE = "org.eclipse.cdt.ui.overrideIndicator"; //$NON-NLS-1$
|
public static final String ANNOTATION_TYPE = "org.eclipse.cdt.ui.overrideIndicator"; //$NON-NLS-1$
|
||||||
private static final String MESSAGE_SEPARATOR = ";\n"; //$NON-NLS-1$
|
private final HashMap<ICPPClassType, ICPPMethod[]> methodsCache = new HashMap<>();
|
||||||
|
|
||||||
public static class OverrideInfo {
|
public static final int ANNOTATION_IMPLEMENTS = 0;
|
||||||
public int nodeOffset;
|
public static final int ANNOTATION_OVERRIDES = 1;
|
||||||
public int resultType;
|
public static final int ANNOTATION_SHADOWS = 2;
|
||||||
public String message;
|
|
||||||
public int nodeLength;
|
|
||||||
|
|
||||||
public IBinding binding;
|
|
||||||
|
|
||||||
public OverrideInfo(int nodeOffset, int nodeLength, int markerType, String message, IBinding binding) {
|
|
||||||
this.nodeOffset = nodeOffset;
|
|
||||||
this.resultType = markerType;
|
|
||||||
this.message = message;
|
|
||||||
this.binding = binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final int RESULT_OVERRIDES = 0;
|
|
||||||
public static final int RESULT_IMPLEMENTS = 1;
|
|
||||||
public static final int RESULT_SHADOWS = 2;
|
|
||||||
|
|
||||||
public class OverrideIndicator extends Annotation {
|
public class OverrideIndicator extends Annotation {
|
||||||
public static final String ANNOTATION_TYPE_ID = "org.eclipse.cdt.ui.overrideIndicator"; //$NON-NLS-1$
|
public static final String ANNOTATION_TYPE_ID = "org.eclipse.cdt.ui.overrideIndicator"; //$NON-NLS-1$
|
||||||
private int type;
|
private final int type;
|
||||||
private ICElementHandle declaration;
|
private final ICElementHandle elementHandle;
|
||||||
|
|
||||||
public OverrideIndicator(int resultType, String message, IBinding binding, IIndex index) {
|
public OverrideIndicator(int resultType, String message, ICElementHandle elementHandle) {
|
||||||
super(ANNOTATION_TYPE_ID, false, message);
|
super(ANNOTATION_TYPE_ID, false, message);
|
||||||
this.type = resultType;
|
this.type = resultType;
|
||||||
try {
|
this.elementHandle = elementHandle;
|
||||||
declaration = IndexUI.findAnyDeclaration(index, null, binding);
|
|
||||||
if (declaration == null) {
|
|
||||||
ICElementHandle[] allDefinitions = IndexUI.findAllDefinitions(index, binding);
|
|
||||||
if (allDefinitions.length > 0) {
|
|
||||||
declaration = allDefinitions[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (CoreException e) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIndicationType() {
|
public int getIndicationType() {
|
||||||
|
@ -102,315 +73,29 @@ public class OverrideIndicatorManager implements ICReconcilingListener {
|
||||||
|
|
||||||
public void open() {
|
public void open() {
|
||||||
try {
|
try {
|
||||||
CDTUITools.openInEditor(declaration, true, true);
|
CDTUITools.openInEditor(elementHandle, true, true);
|
||||||
} catch (CoreException e) {
|
} catch (CoreException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IAnnotationModel fAnnotationModel;
|
private final IAnnotationModel fAnnotationModel;
|
||||||
private Vector<OverrideIndicator> fOverrideAnnotations = new Vector<OverrideIndicator>();
|
private Annotation[] fOverrideAnnotations;
|
||||||
private Object fAnnotationModelLockObject;
|
|
||||||
|
|
||||||
public OverrideIndicatorManager(IAnnotationModel annotationModel) {
|
private final Object fAnnotationModelLockObject;
|
||||||
|
private int annotationKind;
|
||||||
|
private String annotationMessage;
|
||||||
|
|
||||||
|
public OverrideIndicatorManager(IAnnotationModel annotationModel, IASTTranslationUnit ast) {
|
||||||
fAnnotationModel = annotationModel;
|
fAnnotationModel = annotationModel;
|
||||||
fAnnotationModelLockObject = getLockObject(fAnnotationModel);
|
fAnnotationModelLockObject = getLockObject(fAnnotationModel);
|
||||||
}
|
updateAnnotations(ast, new NullProgressMonitor());
|
||||||
|
|
||||||
private void handleResult(OverrideInfo info, IIndex index) {
|
|
||||||
Position position = new Position(info.nodeOffset, info.nodeLength);
|
|
||||||
|
|
||||||
OverrideIndicator indicator = new OverrideIndicator(info.resultType, info.message, info.binding, index);
|
|
||||||
synchronized (fAnnotationModelLockObject) {
|
|
||||||
fAnnotationModel.addAnnotation(indicator, position);
|
|
||||||
}
|
|
||||||
fOverrideAnnotations.add(indicator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all override indicators from this manager's annotation model.
|
|
||||||
*/
|
|
||||||
public void removeAnnotations() {
|
|
||||||
if (fOverrideAnnotations == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
synchronized (fAnnotationModelLockObject) {
|
|
||||||
for (Annotation i : fOverrideAnnotations)
|
|
||||||
fAnnotationModel.removeAnnotation(i);
|
|
||||||
fOverrideAnnotations.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generateAnnotations(IASTTranslationUnit ast, final IIndex index) {
|
|
||||||
|
|
||||||
class MethodDeclarationFinder extends ASTVisitor {
|
|
||||||
{
|
|
||||||
shouldVisitDeclarations = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int visit(IASTDeclaration declaration) {
|
|
||||||
try {
|
|
||||||
IBinding binding = null;
|
|
||||||
ICPPMethod method = null;
|
|
||||||
if (isFunctionDeclaration(declaration)) {
|
|
||||||
binding = getDeclarationBinding(declaration);
|
|
||||||
} else if (declaration instanceof IASTFunctionDefinition) {
|
|
||||||
binding = getDefinitionBinding((IASTFunctionDefinition) declaration);
|
|
||||||
}
|
|
||||||
if (binding instanceof ICPPMethod) {
|
|
||||||
method = (ICPPMethod) binding;
|
|
||||||
OverrideInfo overrideInfo = checkForOverride(method, declaration);
|
|
||||||
if (overrideInfo != null) {
|
|
||||||
handleResult(overrideInfo, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (DOMException e) {
|
|
||||||
}
|
|
||||||
// go to next declaration
|
|
||||||
return PROCESS_SKIP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CompositeTypeFinder extends ASTVisitor {
|
|
||||||
{
|
|
||||||
shouldVisitDeclSpecifiers = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int visit(IASTDeclSpecifier declSpec) {
|
|
||||||
if (declSpec instanceof ICPPASTCompositeTypeSpecifier) {
|
|
||||||
declSpec.accept(new MethodDeclarationFinder());
|
|
||||||
}
|
|
||||||
return PROCESS_CONTINUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MethodDefinitionFinder extends ASTVisitor {
|
|
||||||
{
|
|
||||||
shouldVisitDeclarations = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int visit(IASTDeclaration declaration) {
|
|
||||||
try {
|
|
||||||
if (!(declaration instanceof IASTFunctionDefinition)) {
|
|
||||||
return PROCESS_SKIP;
|
|
||||||
}
|
|
||||||
IASTFunctionDefinition definition = (IASTFunctionDefinition) declaration;
|
|
||||||
IBinding definitionBinding = getDefinitionBinding(definition);
|
|
||||||
if (!(definitionBinding instanceof ICPPMethod)) {
|
|
||||||
return PROCESS_SKIP;
|
|
||||||
}
|
|
||||||
ICPPMethod method = (ICPPMethod) definitionBinding;
|
|
||||||
OverrideInfo overrideInfo = checkForOverride(method, definition);
|
|
||||||
if (overrideInfo != null) {
|
|
||||||
handleResult(overrideInfo, index);
|
|
||||||
}
|
|
||||||
} catch (DOMException e) {
|
|
||||||
}
|
|
||||||
return PROCESS_SKIP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ast.accept(new CompositeTypeFinder());
|
|
||||||
ast.accept(new MethodDefinitionFinder());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static OverrideInfo checkForOverride(ICPPMethod testedOverride, IASTNode node) throws DOMException {
|
|
||||||
IASTFileLocation location = node.getFileLocation();
|
|
||||||
|
|
||||||
boolean onlyPureVirtual = true;
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
Set<ICPPMethod> overridenMethods = new HashSet<ICPPMethod>();
|
|
||||||
Set<ICPPMethod> shadowedMethods = new HashSet<ICPPMethod>();
|
|
||||||
|
|
||||||
Set<ICPPClassType> alreadyTestedBases = new HashSet<ICPPClassType>();
|
|
||||||
|
|
||||||
ICPPBase[] bases = ClassTypeHelper.getBases(testedOverride.getClassOwner(), node);
|
|
||||||
|
|
||||||
// Don't override 'self' in cyclic inheritance
|
|
||||||
alreadyTestedBases.add(testedOverride.getClassOwner());
|
|
||||||
|
|
||||||
for (ICPPBase base : bases) {
|
|
||||||
ICPPClassType testedClass;
|
|
||||||
if (!(base.getBaseClass() instanceof ICPPClassType)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
testedClass = (ICPPClassType) base.getBaseClass();
|
|
||||||
|
|
||||||
overridenMethods.clear();
|
|
||||||
shadowedMethods.clear();
|
|
||||||
handleBaseClass(testedClass, testedOverride, overridenMethods, shadowedMethods,
|
|
||||||
alreadyTestedBases, node);
|
|
||||||
|
|
||||||
for (ICPPMethod overriddenMethod : overridenMethods) {
|
|
||||||
if (sb.length() > 0) {
|
|
||||||
sb.append(MESSAGE_SEPARATOR);
|
|
||||||
}
|
|
||||||
if (overriddenMethod.isPureVirtual()) {
|
|
||||||
sb.append(CEditorMessages.OverrideIndicatorManager_implements);
|
|
||||||
} else {
|
|
||||||
sb.append(CEditorMessages.OverrideIndicatorManager_overrides);
|
|
||||||
onlyPureVirtual = false;
|
|
||||||
}
|
|
||||||
sb.append(' ');
|
|
||||||
sb.append(getQualifiedNameString(overriddenMethod));
|
|
||||||
|
|
||||||
if (bases.length > 1 && overriddenMethod.getClassOwner() != testedClass) {
|
|
||||||
sb.append(' ');
|
|
||||||
sb.append(CEditorMessages.OverrideIndicatorManager_via);
|
|
||||||
sb.append(' ');
|
|
||||||
sb.append(getQualifiedNameString(testedClass));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (ICPPMethod shadowedMethod : shadowedMethods) {
|
|
||||||
if (sb.length() > 0) {
|
|
||||||
sb.append(MESSAGE_SEPARATOR);
|
|
||||||
}
|
|
||||||
sb.append(CEditorMessages.OverrideIndicatorManager_shadows);
|
|
||||||
sb.append(' ');
|
|
||||||
sb.append(getQualifiedNameString(shadowedMethod));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int markerType;
|
|
||||||
if (overridenMethods.size() > 0) {
|
|
||||||
markerType = onlyPureVirtual ? RESULT_IMPLEMENTS : RESULT_OVERRIDES;
|
|
||||||
} else {
|
|
||||||
markerType = RESULT_SHADOWS;
|
|
||||||
}
|
|
||||||
|
|
||||||
IBinding bindingToOpen = null;
|
|
||||||
if (overridenMethods.size() > 0) {
|
|
||||||
bindingToOpen = overridenMethods.iterator().next();
|
|
||||||
} else if (shadowedMethods.size() > 0) {
|
|
||||||
bindingToOpen = shadowedMethods.iterator().next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sb.length() > 0) {
|
|
||||||
OverrideInfo info = new OverrideInfo(location.getNodeOffset(), location.getNodeLength(),
|
|
||||||
markerType, sb.toString(), bindingToOpen);
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the class directly has a valid override for testedOverride, it is added to foundBindings.
|
|
||||||
* Otherwise each base class is added to handleBaseClass.
|
|
||||||
*
|
|
||||||
* @param shadowedMethods
|
|
||||||
* @param alreadyTestedBases
|
|
||||||
* @param point
|
|
||||||
*
|
|
||||||
* @throws DOMException
|
|
||||||
*/
|
|
||||||
private static void handleBaseClass(ICPPClassType classType, ICPPMethod testedOverride,
|
|
||||||
Set<ICPPMethod> foundMethods, Set<ICPPMethod> shadowedMethods,
|
|
||||||
Set<ICPPClassType> alreadyTestedBases, IASTNode point) throws DOMException {
|
|
||||||
if (alreadyTestedBases.contains(classType)) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
alreadyTestedBases.add(classType);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<ICPPMethod> validOverrides = new Vector<ICPPMethod>();
|
|
||||||
for (ICPPMethod method : ClassTypeHelper.getDeclaredMethods(classType, point)) {
|
|
||||||
if (testedOverride.getName().equals(method.getName())) {
|
|
||||||
if (ClassTypeHelper.isOverrider(testedOverride, method)) {
|
|
||||||
validOverrides.add(method);
|
|
||||||
} else if (sameParameters(testedOverride, method)) {
|
|
||||||
shadowedMethods.add(method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (validOverrides.size() > 1) {
|
|
||||||
/* System.err.println("Found many valid overrides"); */
|
|
||||||
}
|
|
||||||
if (validOverrides.size() >= 1) {
|
|
||||||
foundMethods.addAll(validOverrides);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ICPPBase b : ClassTypeHelper.getBases(classType, point)) {
|
|
||||||
if (!(b.getBaseClass() instanceof ICPPClassType)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ICPPClassType baseClass = (ICPPClassType) b.getBaseClass();
|
|
||||||
handleBaseClass(baseClass, testedOverride, foundMethods, shadowedMethods,
|
|
||||||
alreadyTestedBases, point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean sameParameters(ICPPMethod a, ICPPMethod b) throws DOMException {
|
|
||||||
ICPPFunctionType aType = a.getType();
|
|
||||||
ICPPFunctionType bType = b.getType();
|
|
||||||
if (aType.getParameterTypes().length != bType.getParameterTypes().length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < aType.getParameterTypes().length; ++i) {
|
|
||||||
IType overrideParamType = aType.getParameterTypes()[i];
|
|
||||||
IType methodParamType = bType.getParameterTypes()[i];
|
|
||||||
if (!overrideParamType.isSameType(methodParamType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getQualifiedNameString(ICPPBinding binding) throws DOMException {
|
|
||||||
String methodQualifiedName = ASTStringUtil.join(binding.getQualifiedName(), "::"); //$NON-NLS-1$
|
|
||||||
return methodQualifiedName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isFunctionDeclaration(IASTDeclaration declaration) {
|
|
||||||
if (!(declaration instanceof IASTSimpleDeclaration)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) declaration;
|
|
||||||
IASTDeclarator[] declarators = simpleDecl.getDeclarators();
|
|
||||||
if (declarators.length < 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
IASTDeclarator declarator = ASTQueries.findInnermostDeclarator(declarators[0]);
|
|
||||||
return (declarator instanceof IASTFunctionDeclarator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IBinding getDefinitionBinding(IASTFunctionDefinition definition) {
|
|
||||||
IASTDeclarator declarator = ASTQueries.findInnermostDeclarator(definition.getDeclarator());
|
|
||||||
return declarator.getName().resolveBinding();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IBinding getDeclarationBinding(IASTDeclaration declaration) {
|
|
||||||
for (IASTNode node : declaration.getChildren()) {
|
|
||||||
if (node instanceof IASTDeclarator) {
|
|
||||||
IASTDeclarator decl = ASTQueries.findInnermostDeclarator((IASTDeclarator) node);
|
|
||||||
return decl.getName().resolveBinding();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void aboutToBeReconciled() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reconciled(IASTTranslationUnit ast, boolean force, IProgressMonitor progressMonitor) {
|
|
||||||
if (ast == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
IIndex index = ast.getIndex();
|
|
||||||
removeAnnotations();
|
|
||||||
generateAnnotations(ast, index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the lock object for the given annotation model.
|
* Returns the lock object for the given annotation model.
|
||||||
*
|
*
|
||||||
* @param annotationModel
|
* @param annotationModel the annotation model
|
||||||
* the annotation model
|
|
||||||
* @return the annotation model's lock object
|
* @return the annotation model's lock object
|
||||||
*/
|
*/
|
||||||
private Object getLockObject(IAnnotationModel annotationModel) {
|
private Object getLockObject(IAnnotationModel annotationModel) {
|
||||||
|
@ -421,4 +106,200 @@ public class OverrideIndicatorManager implements ICReconcilingListener {
|
||||||
}
|
}
|
||||||
return annotationModel;
|
return annotationModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateAnnotations(IASTTranslationUnit ast, IProgressMonitor progressMonitor) {
|
||||||
|
if (ast == null || progressMonitor.isCanceled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
final IIndex index = ast.getIndex();
|
||||||
|
final Map<Annotation, Position> annotationMap= new HashMap<Annotation, Position>(50);
|
||||||
|
|
||||||
|
class MethodFinder extends ASTVisitor {
|
||||||
|
{
|
||||||
|
shouldVisitDeclarators = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int visit(IASTDeclarator declarator) {
|
||||||
|
if (!(declarator instanceof ICPPASTFunctionDeclarator)) {
|
||||||
|
return PROCESS_CONTINUE;
|
||||||
|
}
|
||||||
|
IASTDeclarator decl = ASTQueries.findInnermostDeclarator(declarator);
|
||||||
|
IBinding binding = decl.getName().resolveBinding();
|
||||||
|
if (binding instanceof ICPPMethod) {
|
||||||
|
ICPPMethod method = (ICPPMethod) binding;
|
||||||
|
try {
|
||||||
|
ICPPMethod overriddenMethod = testForOverride(method, declarator);
|
||||||
|
if (overriddenMethod != null) {
|
||||||
|
try {
|
||||||
|
ICElementHandle baseDeclaration = IndexUI.findAnyDeclaration(index, null, overriddenMethod);
|
||||||
|
if (baseDeclaration == null) {
|
||||||
|
ICElementHandle[] allDefinitions = IndexUI.findAllDefinitions(index, overriddenMethod);
|
||||||
|
if (allDefinitions.length > 0) {
|
||||||
|
baseDeclaration = allDefinitions[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OverrideIndicator indicator = new OverrideIndicator(annotationKind, annotationMessage, baseDeclaration);
|
||||||
|
|
||||||
|
IASTFileLocation fileLocation = declarator.getFileLocation();
|
||||||
|
Position position = new Position(fileLocation.getNodeOffset(), fileLocation.getNodeLength());
|
||||||
|
annotationMap.put(indicator, position);
|
||||||
|
} catch (CoreException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (DOMException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PROCESS_CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ast.accept(new MethodFinder());
|
||||||
|
} finally {
|
||||||
|
methodsCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressMonitor.isCanceled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
synchronized (fAnnotationModelLockObject) {
|
||||||
|
if (fAnnotationModel instanceof IAnnotationModelExtension) {
|
||||||
|
((IAnnotationModelExtension)fAnnotationModel).replaceAnnotations(fOverrideAnnotations, annotationMap);
|
||||||
|
} else {
|
||||||
|
removeAnnotations();
|
||||||
|
for (Map.Entry<Annotation, Position> entry : annotationMap.entrySet()) {
|
||||||
|
fAnnotationModel.addAnnotation(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fOverrideAnnotations= annotationMap.keySet().toArray(new Annotation[annotationMap.keySet().size()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ICPPMethod testForOverride(ICPPMethod method, IASTNode point) throws DOMException {
|
||||||
|
if (method.isDestructor() || method.isPureVirtual()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICPPBase[] bases = ClassTypeHelper.getBases(method.getClassOwner(), point);
|
||||||
|
if (bases.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICPPClassType owningClass = method.getClassOwner();
|
||||||
|
ICPPMethod overriddenMethod = getOverriddenMethodInBaseClass(owningClass, method, point);
|
||||||
|
|
||||||
|
if (overriddenMethod != null) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (annotationKind == ANNOTATION_IMPLEMENTS) {
|
||||||
|
sb.append(CEditorMessages.OverrideIndicatorManager_implements);
|
||||||
|
} else if (annotationKind == ANNOTATION_OVERRIDES){
|
||||||
|
sb.append(CEditorMessages.OverrideIndicatorManager_overrides);
|
||||||
|
} else if (annotationKind == ANNOTATION_SHADOWS) {
|
||||||
|
sb.append(CEditorMessages.OverrideIndicatorManager_shadows);
|
||||||
|
}
|
||||||
|
sb.append(' ');
|
||||||
|
sb.append(ASTStringUtil.join(overriddenMethod.getQualifiedName(), "::")); //$NON-NLS-1$
|
||||||
|
|
||||||
|
if (bases.length > 1) {
|
||||||
|
boolean foundInDirectlyDerivedBaseClass = false;
|
||||||
|
ICPPClassType matchedMethodOwner = overriddenMethod.getClassOwner();
|
||||||
|
for (ICPPBase base : bases) {
|
||||||
|
if (base.getBaseClass() == matchedMethodOwner) {
|
||||||
|
foundInDirectlyDerivedBaseClass = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundInDirectlyDerivedBaseClass) {
|
||||||
|
ICPPClassType indirectingClass = null;
|
||||||
|
for (ICPPBase base : bases) {
|
||||||
|
indirectingClass = (ICPPClassType)base.getBaseClass();
|
||||||
|
if (getOverriddenMethodInBaseClass(indirectingClass, method, point) != null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (indirectingClass != null) {
|
||||||
|
sb.append(' ');
|
||||||
|
sb.append(CEditorMessages.OverrideIndicatorManager_via);
|
||||||
|
sb.append(' ');
|
||||||
|
sb.append(ASTStringUtil.join(indirectingClass.getQualifiedName(), "::")); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
annotationMessage = sb.toString();
|
||||||
|
return overriddenMethod;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ICPPMethod getOverriddenMethodInBaseClass(ICPPClassType aClass, ICPPMethod testedMethod,
|
||||||
|
IASTNode point) throws DOMException {
|
||||||
|
final String testedMethodName = testedMethod.getName();
|
||||||
|
|
||||||
|
ICPPMethod[] allInheritedMethods;
|
||||||
|
if (methodsCache.containsKey(aClass)) {
|
||||||
|
allInheritedMethods = methodsCache.get(aClass);
|
||||||
|
} else {
|
||||||
|
ICPPMethod[] inheritedMethods = null;
|
||||||
|
ICPPClassType[] bases= ClassTypeHelper.getAllBases(aClass, point);
|
||||||
|
for (ICPPClassType base : bases) {
|
||||||
|
inheritedMethods = ArrayUtil.addAll(ICPPMethod.class, inheritedMethods,
|
||||||
|
ClassTypeHelper.getDeclaredMethods(base, point));
|
||||||
|
}
|
||||||
|
allInheritedMethods = ArrayUtil.trim(ICPPMethod.class, inheritedMethods);
|
||||||
|
methodsCache.put(aClass, allInheritedMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ICPPMethod method : allInheritedMethods) {
|
||||||
|
if (method.getName().equals(testedMethodName)) {
|
||||||
|
if (method.isVirtual()) {
|
||||||
|
if (ClassTypeHelper.isOverrider(testedMethod, method)) {
|
||||||
|
if (method.isPureVirtual()) {
|
||||||
|
annotationKind = ANNOTATION_IMPLEMENTS;
|
||||||
|
} else {
|
||||||
|
annotationKind = ANNOTATION_OVERRIDES;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The method has same name as virtual method in base, but does not override
|
||||||
|
// it (e.g. because it has a different signature), it shadows it.
|
||||||
|
annotationKind = ANNOTATION_SHADOWS;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The method has same name and is not virtual, it hides/shadows the method
|
||||||
|
// in the base class.
|
||||||
|
annotationKind = ANNOTATION_SHADOWS;
|
||||||
|
}
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all override indicators from this manager's annotation model.
|
||||||
|
*/
|
||||||
|
void removeAnnotations() {
|
||||||
|
if (fOverrideAnnotations == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
synchronized (fAnnotationModelLockObject) {
|
||||||
|
if (fAnnotationModel instanceof IAnnotationModelExtension) {
|
||||||
|
((IAnnotationModelExtension)fAnnotationModel).replaceAnnotations(fOverrideAnnotations, null);
|
||||||
|
} else {
|
||||||
|
for (int i= 0, length= fOverrideAnnotations.length; i < length; i++)
|
||||||
|
fAnnotationModel.removeAnnotation(fOverrideAnnotations[i]);
|
||||||
|
}
|
||||||
|
fOverrideAnnotations= null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void aboutToBeReconciled() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reconciled(IASTTranslationUnit ast, boolean force, IProgressMonitor progressMonitor) {
|
||||||
|
updateAnnotations(ast, progressMonitor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue