/*******************************************************************************
* Copyright (c) 2013 IBM Corporation
* 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:
* Rafael Medeiros Teixeira <rafaelmt@linux.vnet.ibm.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.linuxtools.internal.valgrind.ui.quickfixes;
import java.util.ArrayList;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.linuxtools.internal.valgrind.core.ValgrindError;
import org.eclipse.linuxtools.internal.valgrind.core.ValgrindStackFrame;
import org.eclipse.linuxtools.internal.valgrind.ui.Messages;
import org.eclipse.linuxtools.internal.valgrind.ui.ValgrindUIPlugin;
import org.eclipse.linuxtools.internal.valgrind.ui.ValgrindViewPart;
import org.eclipse.linuxtools.valgrind.core.IValgrindMessage;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
/**
* Quick-fix for the Wrong deallocation function issue reported by memcheck.
* Switches the deallocation function ("free" or "delete") accordingly.
*
* @author rafaelmt
*/
public class WrongDeallocationResolution extends AbstractValgrindMarkerResolution {
private static final String DELETE = "delete"; //$NON-NLS-1$
private static final String FREE = "free"; //$NON-NLS-1$
private static final String NEW = "new"; //$NON-NLS-1$
@Override
public void apply(IMarker marker, IDocument document) {
try {
IASTNode astNode = getIASTNode(marker, document);
if(astNode != null) {
int nodeLength = astNode.getFileLocation().getNodeLength();
int nodeOffset = astNode.getFileLocation().getNodeOffset();
String content = document.get(nodeOffset, nodeLength);
if(content.contains(DELETE)){
String allocFunction = getAllocFunction(marker, document);
if(allocFunction.contains(NEW)){
content = document.get(nodeOffset, nodeLength).replace(DELETE, DELETE + "[]"); //$NON-NLS-1$
document.replace(nodeOffset, nodeLength, content);
} else {
addParentheses(astNode, document);
if(content.contains("[")){ //$NON-NLS-1$
removeBrackets(astNode, document);
}
content = document.get(nodeOffset, nodeLength).replace(DELETE, FREE);
document.replace(nodeOffset, nodeLength, content);
}
} else if(content.contains(FREE)){
if(getAllocFunction(marker, document).contains("[")){ //$NON-NLS-1$
content = content.concat("[]"); //$NON-NLS-1$
}
content = content.replace(FREE, DELETE);
document.replace(nodeOffset, nodeLength, content);
}
IValgrindMessage message = getMessage(marker);
removeMessage(message.getParent());
ValgrindStackFrame nestedStackFrame = getStackBottom(getNestedStack(message.getParent()));
int nestedLine = nestedStackFrame.getLine();
String nestedFile = nestedStackFrame.getFile();
removeMarker(nestedFile, nestedLine, marker.getType());
marker.delete();
}
} catch (BadLocationException|CoreException e ){
Status status = new Status(IStatus.ERROR, ValgrindUIPlugin.PLUGIN_ID, null, e);
String title = Messages.getString("ValgrindMemcheckQuickFixes.Valgrind_error_title"); //$NON-NLS-1$
String message = Messages.getString("ValgrindMemcheckQuickFixes.Error_applying_quickfix"); //$NON-NLS-1$
showErrorMessage(title, message, status);
}
}
@Override
public String getLabel() {
return Messages.getString("ValgrindMemcheckQuickFixes.Wrong_dealloc_label"); //$NON-NLS-1$
}
/**
* Displays a pop-up indicating that an error occurred
* @param title The title of the pop-up window
* @param message The message of the pop-up window
* @param status {@link IStatus} containing information about the error
*/
private void showErrorMessage(String title, String message, IStatus status){
Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
ErrorDialog.openError(shell, title, message, status);
}
/**
* Adds parentheses to a function call (if necessary)
* @param node {@link IASTNode} containing the function call
* @throws BadLocationException
*/
private void addParentheses(IASTNode node, IDocument document) throws BadLocationException{
IASTNode[] children = node.getChildren();
if(children.length > 0 && !children[0].getRawSignature().contains("(")) { //$NON-NLS-1$
IASTNode childNode = children[0];
int childNodeLength = childNode.getFileLocation().getNodeLength();
int childNodeOffset = childNode.getFileLocation().getNodeOffset();
String childContent = document.get(childNodeOffset, childNodeLength);
String newChild = "(".concat(childContent).concat(")"); //$NON-NLS-1$//$NON-NLS-2$
// Skewed 1 char to left to remove space before parentheses
document.replace(childNodeOffset - 1, childNodeLength + 1, newChild);
}
}
/**
* Returns the allocation function that relates to the given marker
* @param marker {@link IMarker} object that points to where the wrong de-allocation function is
* @return {@link String} object containing the allocation function
* @throws BadLocationException
*/
private String getAllocFunction(IMarker marker, IDocument document) throws BadLocationException {
IValgrindMessage allocMessage = null;
String file = marker.getResource().getName();
int line = marker.getAttribute(IMarker.LINE_NUMBER, 0);
IValgrindMessage[] wrongDeallocMessages = getMessagesByText(Messages.getString("ValgrindMemcheckQuickFixes.Wrong_dealloc_message")); //$NON-NLS-1$
for (IValgrindMessage wrongDeallocMessage : wrongDeallocMessages) {
ValgrindStackFrame stackBottom = getStackBottom(wrongDeallocMessage);
int stackBottomLine = stackBottom.getLine();
String stackBottomFile = stackBottom.getFile();
if(stackBottomLine == line && file != null && stackBottomFile.endsWith(file)){
allocMessage = getStackBottom(getNestedStack(wrongDeallocMessage));
}
}
if(allocMessage instanceof ValgrindStackFrame){
int allocLine = ((ValgrindStackFrame)allocMessage).getLine() - 1;
int allocOffset = document.getLineOffset(allocLine);
int allocLength = document.getLineLength(allocLine);
return document.get(allocOffset, allocLength);
}
return null;
}
/**
* Remove array brackets (if present)
* @param node {@link IASTNode} from which the brackets will be removed
* @throws BadLocationException
*/
private void removeBrackets(IASTNode node, IDocument document) throws BadLocationException{
int nodeLength = node.getFileLocation().getNodeLength();
int nodeOffset = node.getFileLocation().getNodeOffset();
String content = document.get(nodeOffset, nodeLength);
String newContent = content.replace("[","").replace("]",""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-4$
document.replace(nodeOffset, nodeLength, newContent);
}
/**
* Returns all of the messages from the currently active Valgrind view that
* contains a given {@link String} in their description.
* @param text the {@link String} to match the Valgrind messages' descriptions
* @return All messages containing the given text.
*/
private IValgrindMessage[] getMessagesByText(String text) {
ValgrindViewPart valgrindView = ValgrindUIPlugin.getDefault().getView();
ArrayList<IValgrindMessage> foundMessages = new ArrayList<>();
if(valgrindView != null){
IValgrindMessage[] messages = valgrindView.getMessages();
if(messages != null && messages.length != 0){
for (IValgrindMessage message : messages) {
if(message.getText().contains(text)){
foundMessages.add(message);
}
}
}
}
IValgrindMessage[] foundMessagesArray = new IValgrindMessage[foundMessages.size()];
foundMessages.toArray(foundMessagesArray);
return foundMessagesArray;
}
/**
* Return the last nested element from a given {@link IValgrindMessage}, or null if there are
* no nested messages.
* @param message
* @return The {@link ValgrindStackFrame} in the bottom of the nested stack
*/
private ValgrindStackFrame getStackBottom(IValgrindMessage message){
ValgrindStackFrame stackBottom = null;
IValgrindMessage[] children = message.getChildren();
for (IValgrindMessage child : children) {
if(child instanceof ValgrindStackFrame){
stackBottom = (ValgrindStackFrame) child;
}
}
return stackBottom;
}
/**
* Returns the {@link IValgrindMessage} element from the Valgrind View that represents
* a given Marker
* @param marker the marker to which the ValgrindMessage relates
* @return {@link IValgrindMessage} that represents the {@link IMarker}
*/
private IValgrindMessage getMessage(IMarker marker) {
IValgrindMessage message = null;
String file = marker.getResource().getName();
int line = marker.getAttribute(IMarker.LINE_NUMBER, 0);
IValgrindMessage[] wrongDeallocMessages = getMessagesByText(Messages.getString("ValgrindMemcheckQuickFixes.Wrong_dealloc_message")); //$NON-NLS-1$
for (IValgrindMessage wrongDeallocMessage : wrongDeallocMessages) {
ValgrindStackFrame stackBottom = getStackBottom(wrongDeallocMessage);
int stackBottomLine = stackBottom.getLine();
String stackBottomFile = stackBottom.getFile();
if(stackBottomLine == line && file != null && stackBottomFile.endsWith(file)){
message = stackBottom;
}
}
return message;
}
/**
* Returns the nested stack from a given ValgrindMessage in the Valgrind View
* @param message The message from which the stack will be acquired
* @return {@link ValgrindError} object containing the nested stack
*/
private ValgrindError getNestedStack(IValgrindMessage message){
ValgrindError nestedError = null;
IValgrindMessage[] children = message.getChildren();
for (IValgrindMessage child : children) {
if(child instanceof ValgrindError){
nestedError = (ValgrindError)child;
}
}
return nestedError;
}
/**
* Removes marker from file
*
* @param file The file containing the marker
* @param line The line in which the marker that will be removed is
* @param markerType The type of marker to be removed
* @throws CoreException
*/
private void removeMarker(String file, int line, String markerType) throws CoreException {
IMarker[] markers = ResourcesPlugin.getWorkspace().getRoot().findMarkers(markerType, false, IResource.DEPTH_INFINITE);
for (IMarker marker : markers) {
if(marker.getAttribute(IMarker.LINE_NUMBER, 0) == line && marker.getResource().getName().equals(file)){
marker.delete();
}
}
}
/**
* Removes message from Valgrind view.
* @param message The message to be removed
*/
private void removeMessage(IValgrindMessage message){
ValgrindViewPart valgrindView = ValgrindUIPlugin.getDefault().getView();
valgrindView.getMessagesViewer().getTreeViewer().remove(message);
}
}