1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-23 17:05:26 +02:00

Extract Function: find and extract dublicated code

This commit is contained in:
Emanuel Graf 2009-07-13 08:28:21 +00:00
parent 6a9abf764d
commit e5fa863d5c
10 changed files with 1506 additions and 19 deletions

File diff suppressed because it is too large Load diff

View file

@ -261,3 +261,102 @@ int main(){
flags="4" id="org.eclipse.cdt.internal.ui.refactoring.extractfunction.ExtractFunctionRefactoring"
name="exp" project="RegressionTestProject" selection="23,5" visibility="private"/>
</session>
//!ExtractFunctionRefactoringTest duplicates with different Names History Test
//#org.eclipse.cdt.ui.tests.refactoring.RefactoringHistoryTest
//@A.h
#ifndef A_H_
#define A_H_
class A
{
public:
A();
virtual ~A();
int foo();
private:
int help();
};
#endif /*A_H_*/
//=
#ifndef A_H_
#define A_H_
class A
{
public:
A();
virtual ~A();
int foo();
private:
int help();
void exp(int & i);
};
#endif /*A_H_*/
//@A.cpp
#include "A.h"
A::A()
{
}
A::~A()
{
int oo = 99;
++oo;
help();
}int A::foo()
{
int i = 2;
++i;
help();
return i;
}
int A::help()
{
return 42;
}
//=
#include "A.h"
A::A()
{
}
A::~A()
{
int oo = 99;
exp(oo);
}void A::exp(int & i)
{
++i;
help();
}
int A::foo()
{
int i = 2;
exp(i);
return i;
}
int A::help()
{
return 42;
}
//@refScript.xml
<?xml version="1.0" encoding="UTF-8"?>
<session version="1.0">
<refactoring comment="Create method exp" description="Extract Method Refactoring"
fileName="file:$$projectPath$$/A.cpp" flags="4" id="org.eclipse.cdt.internal.ui.refactoring.extractfunction.ExtractFunctionRefactoring"
name="exp" project="RegressionTestProject" replaceDuplicates="true" selection="97,13" visibility="private"/>
</session>

View file

@ -30,6 +30,7 @@ public class ExtractFunctionTestSuite extends TestSuite {
suite.addTest(RefactoringTester.suite("ExtractMethodPreprocessorRefactoringTests", "resources/refactoring/ExtractMethodPreprocessor.rts"));
suite.addTest(RefactoringTester.suite("Extract Function Templates Tests", "resources/refactoring/ExtractFunctionTemplates.rts"));
suite.addTest(RefactoringTester.suite("Extract Method History Test", "resources/refactoring/ExtractMethodHistory.rts"));
suite.addTest(RefactoringTester.suite("Extract Function Dublicates Test", "resources/refactoring/ExtractMethodDuplicates.rts"));
return suite;
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik
* Copyright (c) 2008, 2009 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -40,15 +40,13 @@ public class ExtractFunctionComposite extends Composite {
Group returnGroup = createReturnGroup(nameVisiComp);
createReturnValueChooser(returnGroup, info, ip);
// Disabled for now
//createReplaceCheckBox(nameVisiComp);
createReplaceCheckBox(nameVisiComp);
if (info.getMethodContext().getType() == MethodContext.ContextType.METHOD) {
visibilityPanelSetVisible(true);
}else {
visibilityPanelSetVisible(false);
}
layout();
}
@ -106,13 +104,13 @@ public class ExtractFunctionComposite extends Composite {
}
// private void createReplaceCheckBox(Composite parent) {
// replaceSimilar = new Button(parent, SWT.CHECK | SWT.LEFT);
// GridData buttonLayoutData = new GridData(SWT.None);
// buttonLayoutData.verticalIndent = 5;
// replaceSimilar.setLayoutData(buttonLayoutData);
// replaceSimilar.setText(Messages.ExtractFunctionComposite_ReplaceDuplicates);
// }
private void createReplaceCheckBox(Composite parent) {
replaceSimilar = new Button(parent, SWT.CHECK | SWT.LEFT);
GridData buttonLayoutData = new GridData(SWT.None);
buttonLayoutData.verticalIndent = 5;
replaceSimilar.setLayoutData(buttonLayoutData);
replaceSimilar.setText(Messages.ExtractFunctionComposite_ReplaceDuplicates);
}
public ChooserComposite getReturnChooser() {

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2008 Institute for Software, HSR Hochschule fuer Technik
* Copyright (c) 2008, 2009 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@ -17,6 +17,8 @@ import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
@ -65,9 +67,7 @@ public class ExtractFunctionInputPage extends UserInputWizardPage {
});
}
/* Disable until it works again.
*
* comp.getReplaceSimilarButton().addSelectionListener(new SelectionListener(){
comp.getReplaceSimilarButton().addSelectionListener(new SelectionListener(){
public void widgetDefaultSelected(SelectionEvent e) {
info.setReplaceDuplicates(comp.getReplaceSimilarButton().isEnabled());
@ -77,7 +77,7 @@ public class ExtractFunctionInputPage extends UserInputWizardPage {
widgetDefaultSelected(e);
}
});*/
});
setControl(comp);

View file

@ -13,6 +13,7 @@ package org.eclipse.cdt.internal.ui.refactoring.extractfunction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Vector;
@ -36,6 +37,7 @@ import org.eclipse.text.edits.TextEditGroup;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTComment;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
@ -49,6 +51,7 @@ import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTInitializerExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
@ -63,11 +66,15 @@ import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.IParameter;
import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConversionName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTOperatorName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter;
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.ICPPMethod;
import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
@ -105,6 +112,7 @@ import org.eclipse.cdt.internal.ui.refactoring.NodeContainer;
import org.eclipse.cdt.internal.ui.refactoring.MethodContext.ContextType;
import org.eclipse.cdt.internal.ui.refactoring.NodeContainer.NameInformation;
import org.eclipse.cdt.internal.ui.refactoring.utils.ASTHelper;
import org.eclipse.cdt.internal.ui.refactoring.utils.CPPASTAllVisitor;
import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper;
import org.eclipse.cdt.internal.ui.refactoring.utils.SelectionHelper;
@ -129,7 +137,7 @@ public class ExtractFunctionRefactoring extends CRefactoring {
HashMap<String, Integer> nameTrail;
private ExtractedFunctionConstructionHelper extractedFunctionConstructionHelper;
private INodeFactory factory = CPPNodeFactory.getDefault();
private final INodeFactory factory = CPPNodeFactory.getDefault();
public ExtractFunctionRefactoring(IFile file, ISelection selection,
ExtractFunctionInformation info, ICProject project) {
@ -340,6 +348,11 @@ public class ExtractFunctionRefactoring extends CRefactoring {
methodCall = declarationStatement;
}
insertCallintoTree(methodCall, container.getNodesToWrite(), rewriter, editGroup);
if (info.isReplaceDuplicates()) {
replaceSimilar(collector, astMethodName, implementationFile, context.getType());
}
for (IASTNode node : container.getNodesToWrite()) {
if (node != firstNodeToWrite) {
rewriter.remove(node, editGroup);
@ -404,6 +417,158 @@ public class ExtractFunctionRefactoring extends CRefactoring {
}
private void replaceSimilar(ModificationCollector collector, final IASTName astMethodName,
final IFile implementationFile,
final ContextType contextType) {
// Find similar code
final List<IASTNode> nodesToRewriteWithoutComments = new LinkedList<IASTNode>();
for (IASTNode node : container.getNodesToWrite()) {
if (!(node instanceof IASTComment)) {
nodesToRewriteWithoutComments.add(node);
}
}
final Vector<IASTNode> initTrail = getTrail(nodesToRewriteWithoutComments);
final String title;
if (contextType == MethodContext.ContextType.METHOD) {
title = Messages.ExtractFunctionRefactoring_CreateMethodCall;
} else {
title = Messages.ExtractFunctionRefactoring_CreateFunctionCall;
}
if (!hasNameResolvingForSimilarError) {
unit.accept(new SimilarFinderVisitor(this, collector, initTrail, implementationFile,
astMethodName, nodesToRewriteWithoutComments, title));
}
}
protected Vector<IASTNode> getTrail(List<IASTNode> stmts) {
final Vector<IASTNode> trail = new Vector<IASTNode>();
nameTrail = new HashMap<String, Integer>();
final Container<Integer> trailCounter = new Container<Integer>(NULL_INTEGER);
for (IASTNode node : stmts) {
node.accept(new CPPASTAllVisitor() {
@Override
public int visitAll(IASTNode node) {
if (node instanceof IASTComment) {
// Visit Comment, but don't add them to the trail
return super.visitAll(node);
} else if (node instanceof IASTNamedTypeSpecifier) {
// Skip if somewhere is a named Type Specifier
trail.add(node);
return PROCESS_SKIP;
} else if (node instanceof IASTName) {
if (node instanceof ICPPASTConversionName && node instanceof ICPPASTOperatorName
&& node instanceof ICPPASTTemplateId) {
trail.add(node);
return super.visitAll(node);
} else {
// Save Name Sequenz Number
IASTName name = (IASTName) node;
TrailName trailName = new TrailName();
int actCount = trailCounter.getObject().intValue();
if (nameTrail.containsKey(name.getRawSignature())) {
Integer value = nameTrail.get(name.getRawSignature());
actCount = value.intValue();
} else {
trailCounter.setObject(Integer.valueOf(++actCount));
nameTrail.put(name.getRawSignature(), trailCounter.getObject());
}
trailName.setNameNumber(actCount);
trailName.setRealName(name);
if (info.getReturnVariable() != null
&& info.getReturnVariable().getName().getRawSignature().equals(
name.getRawSignature())) {
returnNumber.setObject(Integer.valueOf(actCount));
}
// Save type informations for the name
IBinding bind = name.resolveBinding();
IASTName[] declNames = name.getTranslationUnit().getDeclarationsInAST(bind);
if (declNames.length > 0) {
IASTNode tmpNode = ASTHelper.getDeclarationForNode(declNames[0]);
IBinding declbind = declNames[0].resolveBinding();
if (declbind instanceof ICPPBinding) {
ICPPBinding cppBind = (ICPPBinding) declbind;
try {
trailName.setGloballyQualified(cppBind.isGloballyQualified());
} catch (DOMException e) {
ILog logger = CUIPlugin.getDefault().getLog();
IStatus status = new Status(IStatus.WARNING, CUIPlugin.PLUGIN_ID,
IStatus.OK, e.getMessage(), e);
logger.log(status);
}
}
if (tmpNode != null) {
trailName.setDeclaration(tmpNode);
} else {
hasNameResolvingForSimilarError = true;
}
}
trail.add(trailName);
return PROCESS_SKIP;
}
} else {
trail.add(node);
return super.visitAll(node);
}
}
});
}
return trail;
}
protected boolean isStatementInTrail(IASTStatement stmt, final Vector<IASTNode> trail) {
final Container<Boolean> same = new Container<Boolean>(Boolean.TRUE);
final TrailNodeEqualityChecker equalityChecker = new TrailNodeEqualityChecker(names, namesCounter);
stmt.accept(new CPPASTAllVisitor() {
@Override
public int visitAll(IASTNode node) {
int pos = trailPos.getObject().intValue();
if (trail.size() <= 0 || pos >= trail.size()) {
same.setObject(Boolean.FALSE);
return PROCESS_ABORT;
}
if (node instanceof IASTComment) {
// Visit Comment, but they are not in the trail
return super.visitAll(node);
}
IASTNode trailNode = trail.get(pos);
trailPos.setObject(Integer.valueOf(pos + 1));
if (equalityChecker.isEquals(trailNode, node)) {
if (node instanceof ICPPASTQualifiedName || node instanceof IASTNamedTypeSpecifier) {
return PROCESS_SKIP;
} else {
return super.visitAll(node);
}
} else {
same.setObject(new Boolean(false));
return PROCESS_ABORT;
}
}
});
return same.getObject().booleanValue();
}
private boolean isMethodAllreadyDefined(
IASTSimpleDeclaration methodDeclaration,
ICPPASTCompositeTypeSpecifier classDeclaration) {
@ -777,6 +942,7 @@ public class ExtractFunctionRefactoring extends CRefactoring {
arguments.put(CRefactoringDescription.SELECTION, region.getOffset() + "," + region.getLength()); //$NON-NLS-1$
arguments.put(ExtractFunctionRefactoringDescription.NAME, info.getMethodName());
arguments.put(ExtractFunctionRefactoringDescription.VISIBILITY, info.getVisibility().toString());
arguments.put(ExtractFunctionRefactoringDescription.REPLACE_DUBLICATES, Boolean.toString(info.isReplaceDuplicates()));
return arguments;
}

View file

@ -32,6 +32,7 @@ import org.eclipse.cdt.internal.ui.refactoring.utils.VisibilityEnum;
public class ExtractFunctionRefactoringDescription extends CRefactoringDescription {
protected static final String NAME = "name"; //$NON-NLS-1$
protected static final String VISIBILITY = "visibility"; //$NON-NLS-1$
protected static final String REPLACE_DUBLICATES = "replaceDuplicates"; //$NON-NLS-1$
public ExtractFunctionRefactoringDescription(String project, String description,
String comment, Map<String, String> arguments) {
@ -46,6 +47,7 @@ public class ExtractFunctionRefactoringDescription extends CRefactoringDescripti
info.setMethodName(arguments.get(NAME));
info.setVisibility(VisibilityEnum.getEnumForStringRepresentation(arguments.get(VISIBILITY)));
info.setReplaceDuplicates(Boolean.parseBoolean(arguments.get(REPLACE_DUBLICATES)));
proj = getCProject();
file = getFile();

View file

@ -34,7 +34,7 @@ public final class Messages extends NLS {
public static String ExtractFunctionComposite_ReturnValue;
public static String ExtractFunctionRefactoring_CreateMethodDef;
public static String ExtractFunctionRefactoring_CreateFunctionDef;
// public static String ExtractFunctionComposite_ReplaceDuplicates;
public static String ExtractFunctionComposite_ReplaceDuplicates;
public static String ExtractFunctionRefactoring_CreateMethodCall;
public static String ExtractFunctionRefactoring_CreateFunctionCall;
public static String ChooserComposite_Return;

View file

@ -0,0 +1,146 @@
/*******************************************************************************
* Copyright (c) 2009 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences 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:
* Institute for Software - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.extractfunction;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.Map.Entry;
import org.eclipse.core.resources.IFile;
import org.eclipse.text.edits.TextEditGroup;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor;
import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector;
import org.eclipse.cdt.internal.ui.refactoring.NodeContainer;
import org.eclipse.cdt.internal.ui.refactoring.NodeContainer.NameInformation;
final class SimilarFinderVisitor extends CPPASTVisitor {
private final ExtractFunctionRefactoring refactoring;
private final Vector<IASTNode> trail;
private final IASTName name;
private final List<IASTNode> stmts;
private int i = 0;
private NodeContainer similarContainer;
private final List<IASTStatement> stmtToReplace = new ArrayList<IASTStatement>();
private final ModificationCollector collector;
SimilarFinderVisitor(ExtractFunctionRefactoring refactoring,
ModificationCollector collector, Vector<IASTNode> trail, IFile file, IASTName name,
List<IASTNode> stmts, String title) {
this.refactoring = refactoring;
this.trail = trail;
this.name = name;
this.stmts = stmts;
this.collector = collector;
this.similarContainer = new NodeContainer();
}
{
shouldVisitStatements = true;
}
@Override
public int visit(IASTStatement stmt) {
boolean isAllreadyInMainRefactoring = isInSelection(stmt);
if( (!isAllreadyInMainRefactoring)
&& this.refactoring.isStatementInTrail(stmt, trail)){
stmtToReplace.add(stmt);
similarContainer.add(stmt);
++i;
if(i==stmts.size()){
//found similar code
boolean similarOnReturnWays = true;
for (NameInformation nameInfo : similarContainer.getAllAfterUsedNames()) {
if(this.refactoring.names.containsKey(nameInfo.getDeclaration().getRawSignature())){
Integer nameOrderNumber = this.refactoring.names.get(nameInfo.getDeclaration().getRawSignature());
if(this.refactoring.nameTrail.containsValue(nameOrderNumber)){
String orgName = null;
boolean found = false;
for (Entry<String, Integer> entry : this.refactoring.nameTrail.entrySet()) {
if(entry.getValue().equals(nameOrderNumber)){
orgName = entry.getKey();
}
}
if(orgName != null){
for (NameInformation orgNameInfo : this.refactoring.container.getAllAfterUsedNamesChoosenByUser()) {
if( orgName.equals(orgNameInfo.getDeclaration().getRawSignature()) ){
found = true;
}
}
}
if(!found){
similarOnReturnWays = false;
}
}
}
}
if(similarOnReturnWays){
System.out.println(6);
IASTNode call = refactoring.getMethodCall(name,
this.refactoring.nameTrail, this.refactoring.names,
this.refactoring.container, similarContainer);
ASTRewrite rewrite = collector.rewriterForTranslationUnit(stmtToReplace.get(0)
.getTranslationUnit());
TextEditGroup editGroup = new TextEditGroup("Replace Dublicated Code");
rewrite.replace(stmtToReplace.get(0), call, editGroup);
if (stmtToReplace.size() > 1) {
for (int i = 1; i < stmtToReplace.size(); ++i) {
rewrite.remove(stmtToReplace.get(i), editGroup);
}
}
}
clear();
}
return PROCESS_SKIP;
} else {
clear();
return super.visit(stmt);
}
}
private boolean isInSelection(IASTStatement stmt) {
List<IASTNode>nodes = this.refactoring.container.getNodesToWrite();
for (IASTNode node : nodes) {
if(node.equals(stmt)) {
return true;
}
}
return false;
}
private void clear() {
i = 0;
this.refactoring.names.clear();
similarContainer = new NodeContainer();
this.refactoring.namesCounter.setObject(ExtractFunctionRefactoring.NULL_INTEGER);
this.refactoring.trailPos.setObject(ExtractFunctionRefactoring.NULL_INTEGER);
stmtToReplace.clear();
}
}

View file

@ -22,7 +22,7 @@ ExtractFunctionInputPage_1=is used after the extracted block - it needs to be pa
ExtractFunctionComposite_ReturnValue=Return value:
ExtractFunctionRefactoring_CreateMethodDef=Create Method Definition
ExtractFunctionRefactoring_CreateFunctionDef=Create Function Definition
#ExtractFunctionComposite_ReplaceDuplicates=Replace all occurrences of statements with method.
ExtractFunctionComposite_ReplaceDuplicates=Replace all occurrences of statements with method.
ExtractFunctionRefactoring_CreateMethodCall=Create Method Call
ExtractFunctionRefactoring_CreateFunctionCall=Create Function Call
ChooserComposite_Return=Return