1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Bug 312736: extract variable refactoring does not infer type or replace expression correctly

https://bugs.eclipse.org/bugs/show_bug.cgi?id=312736
This commit is contained in:
Emanuel Graf 2010-07-01 06:44:46 +00:00
parent 2d7b307308
commit eea33f080c
4 changed files with 386 additions and 9 deletions

View file

@ -0,0 +1,49 @@
/*******************************************************************************
* 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.core.dom.rewrite;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.internal.core.dom.rewrite.DeclarationGeneratorImpl;
/**
*
* This class handles the creation of {@link IASTDeclarator}s and {@link IASTDeclSpecifier}s basing on given type.
*
* @author Tomasz Wesolowski
* @noextend This interface is not intended to be extended by clients.
* @since 5.3
*/
public abstract class DeclarationGenerator {
public static DeclarationGenerator create(INodeFactory factory) {
return new DeclarationGeneratorImpl(factory);
}
/**
* Creates a new {@link IASTDeclSpecifier}DeclSpecifier for a given {@link IType}.
* @param type the type to describe
* @return the generated declaration specifier
*/
public abstract IASTDeclSpecifier createDeclSpecFromType(IType type);
/**
* Creates a new {@link IASTDeclarator}Declarator for a given {@link IType}.
* @param type the type to describe
* @param name the name for the declarator
* @return the generated declarator
*/
public abstract IASTDeclarator createDeclaratorFromType(IType type, char[] name);
}

View file

@ -0,0 +1,255 @@
/*******************************************************************************
* 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.internal.core.dom.rewrite;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTPointer;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IArrayType;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IQualifierType;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTReferenceOperator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNodeFactory;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
import org.eclipse.cdt.core.dom.rewrite.DeclarationGenerator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTArrayDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
/**
*
* @author Tomasz Wesolowski
*
*/
public class DeclarationGeneratorImpl extends DeclarationGenerator {
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.rewrite.IDeclarationGenerator#createDeclSpecFromType(org.eclipse.cdt.core.dom.ast.IType)
*/
private INodeFactory factory;
/**
* Creates a new generator using the given factory.
* @param factory a factory to use. If a C++ type is requested, it has to be an instance of {@link ICPPNodeFactory}.
*/
public DeclarationGeneratorImpl(INodeFactory factory) {
this.factory = factory;
}
@Override
public IASTDeclSpecifier createDeclSpecFromType(IType type) {
IASTDeclSpecifier returnedDeclSpec = null;
if (type instanceof IPointerType) {
IType inner = ((IPointerType)type).getType();
returnedDeclSpec = createDeclSpecFromType(inner);
} else if (type instanceof ICPPReferenceType) {
IType inner = ((ICPPReferenceType)type).getType();
returnedDeclSpec = createDeclSpecFromType(inner);
} else if (type instanceof IArrayType) {
IType inner = ((IArrayType)type).getType();
returnedDeclSpec = createDeclSpecFromType(inner);
} else if (type instanceof IFunctionType) {
IType inner = ((IFunctionType)type).getReturnType();
returnedDeclSpec = createDeclSpecFromType(inner);
} else if (type instanceof IQualifierType) {
IType inner = ((IQualifierType)type).getType();
returnedDeclSpec = createDeclSpecFromType(inner);
if (((IQualifierType) type).isConst()) {
returnedDeclSpec.setConst(true);
}
if (((IQualifierType) type).isVolatile()) {
returnedDeclSpec.setVolatile(true);
}
} else if (type instanceof IBasicType) {
Kind kind = ((IBasicType)type).getKind();
IASTSimpleDeclSpecifier declSpec = factory.newSimpleDeclSpecifier();
declSpec.setType(kind);
returnedDeclSpec = declSpec;
} else if (type instanceof IBinding) { /* ITypedef, ICompositeType... */
// BTW - we need to distinguish (and fail explicitly) on literal composites like:
// struct { } aSingleInstance;
returnedDeclSpec = getDeclSpecForBinding((IBinding) type);
}
// Fallback...
if (returnedDeclSpec == null) {
IASTSimpleDeclSpecifier specifier = factory.newSimpleDeclSpecifier();
specifier.setType(Kind.eVoid);
returnedDeclSpec = specifier;
}
return returnedDeclSpec;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.core.dom.rewrite.IDeclarationGenerator#createDeclaratorFromType(org.eclipse.cdt.core.dom.ast.IType, char[])
*/
@Override
public IASTDeclarator createDeclaratorFromType(IType type, char[] name) {
IASTDeclarator returnedDeclarator = null;
try {
if (type instanceof IPointerType || type instanceof IArrayType || type instanceof ICPPReferenceType) {
IASTDeclarator declarator = createPointerOrArrayDeclarator(type, name);
returnedDeclarator = declarator;
} else if (typeIrrelevantForDeclarator(type)) {
IASTDeclarator declarator = factory.newDeclarator(null);
IASTName astName = factory.newName(name);
declarator.setName(astName);
returnedDeclarator = declarator;
} else if (type instanceof IFunctionType) {
// TODO function pointers
}
} catch (DOMException e) {
e.printStackTrace();
}
// Fallback
if (returnedDeclarator == null) {
returnedDeclarator = factory.newDeclarator(factory.newName(name));
}
return returnedDeclarator;
}
/**
* Checks if a given type isn't described by the {@link IASTDeclarator} (only by the {@link IASTDeclSpecifier}).
* @param type the type to check
* @return true if irrelevant
*/
private boolean typeIrrelevantForDeclarator(IType type) {
return type instanceof IBasicType || type instanceof ICompositeType || type instanceof IQualifierType || type instanceof ITypedef;
}
/**
* Handles the creation of declarators of pointers, references, arrays and arrays of pointers/references.
* @param type the topmost type, expected {@link IPointerType}, {@link ICPPReferenceType} or {@link IArrayType}
* @param name the declarator name
* @return Either a {@link CPPASTDeclarator} or a {@link CPPASTArrayDeclarator}, depending on what's needed
* @throws DOMException
*/
private IASTDeclarator createPointerOrArrayDeclarator(IType type, char[] name) throws DOMException {
List<IASTPointerOperator> ptrs = new ArrayList<IASTPointerOperator>();
List<IASTExpression> arrs = new ArrayList<IASTExpression>();
while (type instanceof IPointerType || type instanceof ICPPReferenceType) {
IASTPointerOperator astPtr;
if (type instanceof IPointerType) {
IASTPointer cppastPointer = factory.newPointer();
IPointerType ptrType = (IPointerType) type;
cppastPointer.setConst(ptrType.isConst());
cppastPointer.setVolatile(ptrType.isVolatile());
astPtr = cppastPointer;
type = ptrType.getType();
} else {
ICPPASTReferenceOperator cppastReferenceOperator = ((ICPPNodeFactory)factory).newReferenceOperator(false);
ICPPReferenceType refType = (ICPPReferenceType) type;
astPtr = cppastReferenceOperator;
type = refType.getType();
}
ptrs.add(astPtr);
}
while (type instanceof IArrayType) {
IArrayType arrType = (IArrayType) type;
IASTExpression arraySizeExpression = arrType.getArraySizeExpression();
arrs.add(arraySizeExpression);
type = arrType.getType();
}
IASTDeclarator decl;
if (!arrs.isEmpty()) {
IASTArrayDeclarator arrayDeclarator = factory.newArrayDeclarator(null);
for (IASTExpression exp : arrs) {
IASTArrayModifier arrayModifier = factory.newArrayModifier((exp == null) ? exp : exp.copy());
arrayDeclarator.addArrayModifier(arrayModifier);
}
decl = arrayDeclarator;
} else {
decl = factory.newDeclarator(null);
}
for (IASTPointerOperator ptr : ptrs) {
decl.addPointerOperator(ptr);
}
createDeclaratorContent(decl, type, name);
return decl;
}
/**
* Fills a given declarator with either an ASTName or a proper nested declarator
* @param decl The declarator to fill
* @param type the content type of declarator
* @param name the desired name of declarator tree
* @throws DOMException
*/
private void createDeclaratorContent(IASTDeclarator decl, IType type, char[] name) throws DOMException {
if (type instanceof IPointerType || type instanceof IArrayType || type instanceof ICPPReferenceType) {
IASTDeclarator nested = createPointerOrArrayDeclarator(type, name);
decl.setNestedDeclarator(nested);
} else if (typeIrrelevantForDeclarator(type)) {
IASTName astName = factory.newName(name);
decl.setName(astName);
}
}
private IASTNamedTypeSpecifier getDeclSpecForBinding(IBinding binding) {
char[][] qualifiedNameCharArray = CPPVisitor.getQualifiedNameCharArray(binding);
if (qualifiedNameCharArray.length > 1) {
ICPPASTQualifiedName name = ((ICPPNodeFactory)factory).newQualifiedName();
for (char[] cs : qualifiedNameCharArray) {
name.addName(factory.newName(cs));
}
return factory.newTypedefNameSpecifier(name);
} else if (qualifiedNameCharArray.length == 1) {
IASTName name = factory.newName(qualifiedNameCharArray[0]);
return factory.newTypedefNameSpecifier(name);
} else {
IASTName name = factory.newName(binding.getName().toCharArray());
return factory.newTypedefNameSpecifier(name);
}
}
}

View file

@ -328,3 +328,75 @@ void foo(){
for(int n = i; n < 10; ++n);
}
//!ExtractLocalVariableRefactoringTest expression
//#org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVariableRefactoringTest
//@A.cpp
void foo(){
int a = 0;
float b = 0.1f;
double c = /*$*/(a + b)/*$$*/ * 0.2;
}
//=
void foo(){
int a = 0;
float b = 0.1f;
float a0 = a + b;
double c = a0 * 0.2;
}
//!ExtractLocalVariableRefactoringTest pointer
//#org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVariableRefactoringTest
//@A.cpp
void foo(){
int a[2];
int b = */*$*/(a + 1)/*$$*/;
}
//=
void foo(){
int a[2];
int *i = a + 1;
int b = *i;
}
//!ExtractLocalVariableRefactoringTest qualifiers
//#org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVariableRefactoringTest
//@A.cpp
const volatile int *k;
void foo(){
/*$*/k;/*$$*/
}
//=
const volatile int *k;
void foo(){
const volatile int *k0 = k;
k0;
}
//!ExtractLocalVariableRefactoringTest overloaded operators
//#org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVariableRefactoringTest
//@A.cpp
class K {
public:
bool operator+(int b) {return true;}
float operator+(unsigned u) {return 1.0f;}
};
void foo(){
K k;
/*$*/k+3u/*$$*/;
}
//=
class K {
public:
bool operator+(int b) {return true;}
float operator+(unsigned u) {return 1.0f;}
};
void foo(){
K k;
float i = k + 3u;
i;
}

View file

@ -44,19 +44,19 @@ import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.cdt.core.dom.rewrite.DeclarationGenerator;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarationStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTEqualsInitializer;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTIdExpression;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTLiteralExpression;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTName;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTSimpleDeclaration;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPFunction;
import org.eclipse.cdt.internal.ui.refactoring.CRefactoring;
@ -64,7 +64,6 @@ import org.eclipse.cdt.internal.ui.refactoring.CRefactoringDescription;
import org.eclipse.cdt.internal.ui.refactoring.ModificationCollector;
import org.eclipse.cdt.internal.ui.refactoring.NameNVisibilityInformation;
import org.eclipse.cdt.internal.ui.refactoring.NodeContainer;
import org.eclipse.cdt.internal.ui.refactoring.extractfunction.ExtractExpression;
import org.eclipse.cdt.internal.ui.refactoring.utils.NodeHelper;
import org.eclipse.cdt.internal.ui.refactoring.utils.SelectionHelper;
@ -297,16 +296,18 @@ public class ExtractLocalVariableRefactoring extends CRefactoring {
}
private IASTDeclarationStatement getVariableNodes(String newName) {
IASTSimpleDeclaration simple = new CPPASTSimpleDeclaration();
INodeFactory factory = this.unit.getASTNodeFactory();
IASTSimpleDeclaration simple = factory.newSimpleDeclaration(null);
IASTDeclSpecifier declSpec = new ExtractExpression()
.determineReturnType(deblock(target), null);
DeclarationGenerator generator = DeclarationGenerator.create(factory);
IASTDeclSpecifier declSpec = generator.createDeclSpecFromType(target.getExpressionType());
declSpec.setStorageClass(IASTDeclSpecifier.sc_unspecified);
simple.setDeclSpecifier(declSpec);
IASTDeclarator decl = new CPPASTDeclarator();
IASTName name = new CPPASTName(newName.toCharArray());
decl.setName(name);
IASTDeclarator decl = generator.createDeclaratorFromType(target.getExpressionType(), newName.toCharArray());
IASTEqualsInitializer init = new CPPASTEqualsInitializer();
init.setInitializerClause(deblock(target.copy()));