diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AnnotationTestCase.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AnnotationTestCase.java new file mode 100644 index 00000000000..d09820dc829 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/AnnotationTestCase.java @@ -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 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; + } + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/BaseUITestCase.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/BaseUITestCase.java index 6979a04788c..ab45b8a2a3e 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/BaseUITestCase.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/BaseUITestCase.java @@ -53,7 +53,6 @@ import org.eclipse.cdt.ui.testplugin.CTestPlugin; import org.eclipse.cdt.ui.testplugin.util.StringAsserts; public class BaseUITestCase extends BaseTestCase { - public BaseUITestCase() { super(); } @@ -61,10 +60,11 @@ public class BaseUITestCase extends BaseTestCase { public BaseUITestCase(String name) { super(name); } - + @Override protected void setUp() throws Exception { super.setUp(); + final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); IViewPart view= activePage.findView("org.eclipse.cdt.ui.tests.DOMAST.DOMAST"); if (view != null) { @@ -111,8 +111,9 @@ public class BaseUITestCase extends BaseTestCase { protected void runEventQueue(int time) { final long endTime= System.currentTimeMillis() + time; while (true) { - while (Display.getCurrent().readAndDispatch()) - ; + while (Display.getCurrent().readAndDispatch()) { + } + long diff= endTime - System.currentTimeMillis(); if (diff <= 0) { break; @@ -216,7 +217,7 @@ public class BaseUITestCase extends BaseTestCase { } 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); hs.executeCommand(commandID, null); } @@ -316,7 +317,7 @@ public class BaseUITestCase extends BaseTestCase { try { TreeItem firstItem= i0Node.getItem(0); firstItemText= firstItem.getText(); - if (firstItemText.length() > 0 && !firstItemText.equals("...")) { + if (!firstItemText.isEmpty() && !firstItemText.equals("...")) { TreeItem item = i0Node.getItem(i1); nodePresent = true; 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 // 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 -// for it to not appear should be more than adequate +// for it to not appear should be more than adequate. diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/TestUtils.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/TestUtils.java new file mode 100644 index 00000000000..e9ffd8dc8a7 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/TestUtils.java @@ -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; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/UICoreTestActivator.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/UICoreTestActivator.java new file mode 100644 index 00000000000..67fb0c52c51 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/UICoreTestActivator.java @@ -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; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/UITestCaseWithProject.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/UITestCaseWithProject.java new file mode 100644 index 00000000000..24db7ee9b55 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/UITestCaseWithProject.java @@ -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 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()); + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/OverrideIndicatorTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/OverrideIndicatorTest.java new file mode 100644 index 00000000000..b342ef35058 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/OverrideIndicatorTest.java @@ -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); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java index 2b91ba52433..01d203d8553 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java @@ -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 * are made available under the terms of the Eclipse Public License v1.0 * 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. // @Override + @Override protected IFormattingContext createFormattingContext(int selectionOffset, int selectionLength) { return createFormattingContext(selectionOffset, selectionLength, true); } @@ -600,8 +601,8 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi private boolean fCloseAngularBrackets = true; private boolean fCloseBraces = true; private final String CATEGORY = toString(); - private IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY); - private Deque fBracketLevelStack = new ArrayDeque<>(); + private final IPositionUpdater fUpdater = new ExclusivePositionUpdater(CATEGORY); + private final Deque fBracketLevelStack = new ArrayDeque<>(); public void setCloseBracketsEnabled(boolean enabled) { fCloseBrackets = enabled; @@ -1275,7 +1276,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi protected CPairMatcher fBracketMatcher = new CPairMatcher(BRACKETS); /** 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 */ private CEditorErrorTickUpdater fCEditorErrorTickUpdater; @@ -1320,7 +1321,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi * AST reconciling listeners. * @since 4.0 */ - private ListenerList fReconcilingListeners= new ListenerList(ListenerList.IDENTITY); + private final ListenerList fReconcilingListeners= new ListenerList(ListenerList.IDENTITY); /** * Semantic highlighting manager @@ -3602,7 +3603,7 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi if (model == null) return; - fOverrideIndicatorManager= new OverrideIndicatorManager(model); + fOverrideIndicatorManager= new OverrideIndicatorManager(model, null); addReconcileListener(fOverrideIndicatorManager); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OverrideIndicatorImageProvider.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OverrideIndicatorImageProvider.java index 5a42a7d1023..3c4af0b5c49 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OverrideIndicatorImageProvider.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OverrideIndicatorImageProvider.java @@ -1,12 +1,12 @@ /******************************************************************************* - * Copyright (c) 2010 Tomasz Wesolowski and others + * Copyright (c) 2010, 2015 Tomasz Wesolowski 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: - * Tomasz Wesolowski - initial API and implementation + * Tomasz Wesolowski - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.ui.editor; @@ -19,46 +19,34 @@ import org.eclipse.cdt.internal.ui.CPluginImages; /** * @author Tomasz Wesolowski - * */ -public class OverrideIndicatorImageProvider implements - IAnnotationImageProvider { - +public class OverrideIndicatorImageProvider implements IAnnotationImageProvider { 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 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 public Image getManagedImage(Annotation annotation) { return null; } - /* (non-Javadoc) - * @see org.eclipse.ui.texteditor.IAnnotationImageProvider#getImageDescriptorId(org.eclipse.jface.text.source.Annotation) - */ @Override public String getImageDescriptorId(Annotation annotation) { if (!isImageProviderFor(annotation)) { return null; } switch (getAnnotationType(annotation)) { - case OverrideIndicatorManager.RESULT_OVERRIDES: + case OverrideIndicatorManager.ANNOTATION_OVERRIDES: return OVERRIDE_IMG_DESC_ID; - case OverrideIndicatorManager.RESULT_IMPLEMENTS: + case OverrideIndicatorManager.ANNOTATION_IMPLEMENTS: return IMPLEMENT_IMG_DESC_ID; - case OverrideIndicatorManager.RESULT_SHADOWS: + case OverrideIndicatorManager.ANNOTATION_SHADOWS: return SHADOW_IMG_DESC_ID; } assert false; return null; } - /* (non-Javadoc) - * @see org.eclipse.ui.texteditor.IAnnotationImageProvider#getImageDescriptor(java.lang.String) - */ @Override public ImageDescriptor getImageDescriptor(String imageDescritporId) { if (imageDescritporId.equals(OVERRIDE_IMG_DESC_ID)) { @@ -79,5 +67,4 @@ public class OverrideIndicatorImageProvider implements private int getAnnotationType(Annotation annotation) { return ((OverrideIndicatorManager.OverrideIndicator)annotation).getIndicationType(); } - } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OverrideIndicatorManager.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OverrideIndicatorManager.java index 93f338dbb21..8777b7b55a1 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OverrideIndicatorManager.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OverrideIndicatorManager.java @@ -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 * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -8,40 +8,36 @@ * Contributors: * Tomasz Wesolowski - initial API and implementation * Sergey Prigogin (Google) + * Patrick Hofer [bug 345872] + * Nathan Ridge [bug 345872] *******************************************************************************/ package org.eclipse.cdt.internal.ui.editor; -import java.util.HashSet; -import java.util.Set; -import java.util.Vector; +import java.util.HashMap; +import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; 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.jface.text.source.IAnnotationModelExtension; import org.eclipse.cdt.core.dom.ast.ASTVisitor; 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.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.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IBinding; -import org.eclipse.cdt.core.dom.ast.IType; -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.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.ICPPFunctionType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; 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.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; public class OverrideIndicatorManager implements ICReconcilingListener { - static final String ANNOTATION_TYPE = "org.eclipse.cdt.ui.overrideIndicator"; //$NON-NLS-1$ - private static final String MESSAGE_SEPARATOR = ";\n"; //$NON-NLS-1$ + public static final String ANNOTATION_TYPE = "org.eclipse.cdt.ui.overrideIndicator"; //$NON-NLS-1$ + private final HashMap methodsCache = new HashMap<>(); - public static class OverrideInfo { - public int nodeOffset; - public int resultType; - 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 static final int ANNOTATION_IMPLEMENTS = 0; + public static final int ANNOTATION_OVERRIDES = 1; + public static final int ANNOTATION_SHADOWS = 2; public class OverrideIndicator extends Annotation { public static final String ANNOTATION_TYPE_ID = "org.eclipse.cdt.ui.overrideIndicator"; //$NON-NLS-1$ - private int type; - private ICElementHandle declaration; + private final int type; + 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); this.type = resultType; - try { - 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) { - } + this.elementHandle = elementHandle; } public int getIndicationType() { @@ -102,315 +73,29 @@ public class OverrideIndicatorManager implements ICReconcilingListener { public void open() { try { - CDTUITools.openInEditor(declaration, true, true); + CDTUITools.openInEditor(elementHandle, true, true); } catch (CoreException e) { } } } - private IAnnotationModel fAnnotationModel; - private Vector fOverrideAnnotations = new Vector(); - private Object fAnnotationModelLockObject; - - public OverrideIndicatorManager(IAnnotationModel annotationModel) { + private final IAnnotationModel fAnnotationModel; + private Annotation[] fOverrideAnnotations; + + private final Object fAnnotationModelLockObject; + private int annotationKind; + private String annotationMessage; + + public OverrideIndicatorManager(IAnnotationModel annotationModel, IASTTranslationUnit ast) { fAnnotationModel = annotationModel; fAnnotationModelLockObject = getLockObject(fAnnotationModel); - } - - 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 overridenMethods = new HashSet(); - Set shadowedMethods = new HashSet(); - - Set alreadyTestedBases = new HashSet(); - - 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 foundMethods, Set shadowedMethods, - Set alreadyTestedBases, IASTNode point) throws DOMException { - if (alreadyTestedBases.contains(classType)) { - return; - } else { - alreadyTestedBases.add(classType); - } - - Vector validOverrides = new Vector(); - 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); + updateAnnotations(ast, new NullProgressMonitor()); } /** * Returns the lock object for the given annotation model. - * - * @param annotationModel - * the annotation model + * + * @param annotationModel the annotation model * @return the annotation model's lock object */ private Object getLockObject(IAnnotationModel annotationModel) { @@ -421,4 +106,200 @@ public class OverrideIndicatorManager implements ICReconcilingListener { } return annotationModel; } + + protected void updateAnnotations(IASTTranslationUnit ast, IProgressMonitor progressMonitor) { + if (ast == null || progressMonitor.isCanceled()) + return; + + final IIndex index = ast.getIndex(); + final Map annotationMap= new HashMap(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 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); + } }