/******************************************************************************* * Copyright (c) 2010 Tomasz Wesolowski * 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 *******************************************************************************/ package org.eclipse.cdt.codan.internal.checkers.ui.quickfix; import org.eclipse.cdt.codan.core.cxx.CxxAstUtils; import org.eclipse.cdt.codan.internal.checkers.ui.CheckersUiActivator; import org.eclipse.cdt.codan.ui.AbstractAstRewriteQuickFix; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier.ICPPASTBaseSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel; import org.eclipse.cdt.core.dom.rewrite.ASTRewrite; import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.core.resources.IMarker; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.ltk.core.refactoring.Change; public class QuickFixCreateField extends AbstractAstRewriteQuickFix { public String getLabel() { return Messages.QuickFixCreateField_0; } @Override public void modifyAST(IIndex index, IMarker marker) { CxxAstUtils utils = CxxAstUtils.getInstance(); try { IASTTranslationUnit ast = getTranslationUnitViaEditor(marker).getAST(index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS); IASTName astName = getASTNameFromMarker(marker, ast); if (astName == null) { return; } IASTDeclaration declaration = utils.createDeclaration(astName, ast.getASTNodeFactory(), index); IASTCompositeTypeSpecifier targetCompositeType = utils.getEnclosingCompositeTypeSpecifier(astName); if (targetCompositeType == null) { // We're not in an inline method; // check if we're in a method at all targetCompositeType = utils.getCompositeTypeFromFunction(utils.getEnclosingFunction(astName), index); if (targetCompositeType == null) { return; } } ASTRewrite r = ASTRewrite.create(targetCompositeType.getTranslationUnit()); IASTNode where = findInsertionPlace(targetCompositeType); r.insertBefore(targetCompositeType, where, declaration, null); Change c = r.rewriteAST(); c.perform(new NullProgressMonitor()); } catch (CoreException e) { CheckersUiActivator.log(e); } } /** * Suggests a default place to insert a field: * * Default place to insert: * <ul> * <li>If in a class, after last private field or at the end</li> * <li>If in a struct, after last public field or at the end</li> * </ul> * * @param composite * the composite to search * @return an ASTNode inside composite to insert before, or null to insert * at the end */ protected IASTNode findInsertionPlace(IASTCompositeTypeSpecifier composite) { boolean wantPublicContext; boolean inDesiredAccessibilityContext; // Default: private context for classes, public otherwise wantPublicContext = !(composite.getKey() == ICPPASTCompositeTypeSpecifier.k_class); inDesiredAccessibilityContext = true; IASTNode bestMatch = null; IASTNode[] children = composite.getChildren(); // Get initial candidate at the beginning (after class name and // composite type specifiers) for (IASTNode child : children) { if (child instanceof IASTName || child instanceof ICPPASTBaseSpecifier) { continue; } bestMatch = child; break; } // Check the class body for a better place (i.e. after the last variable // declaration in the expected access scope) for (int i = 0; i < children.length; ++i) { IASTNode child = children[i]; if (child instanceof ICPPASTVisibilityLabel) { ICPPASTVisibilityLabel label = (ICPPASTVisibilityLabel) child; inDesiredAccessibilityContext = (wantPublicContext && label.getVisibility() == ICPPASTVisibilityLabel.v_public) || (!wantPublicContext && label.getVisibility() == ICPPASTVisibilityLabel.v_private); } else if (inDesiredAccessibilityContext && (child instanceof IASTDeclaration) && !(child instanceof IASTFunctionDefinition)) { // TODO: the above condition needs to also check if child is not // a typedef for (IASTNode gchild : child.getChildren()) { if ((gchild instanceof IASTDeclarator) && !(gchild instanceof IASTFunctionDeclarator)) { // Before the next node or at the end (= after the // current node) bestMatch = (i + 1 < children.length) ? children[i + 1] : null; break; } } } } return bestMatch; } @Override public boolean isApplicable(IMarker marker) { String problemArgument = getProblemArgument(marker, 1); return problemArgument.contains(":class") && problemArgument.contains(":func"); //$NON-NLS-1$ //$NON-NLS-2$ } }