/******************************************************************************* * 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); } }