/*******************************************************************************
* Copyright (c) 2010, 2016 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
* Thomas Corbat (IFS)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.rewrite;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator;
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.IASTNode.CopyStyle;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
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.IASTStandardFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
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.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.cpp.ICPPASTName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTPointerToMember;
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.ICPPASTTemplateId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNodeFactory;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPPointerToMemberType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance;
import org.eclipse.cdt.core.dom.rewrite.DeclarationGenerator;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
/**
* @author Tomasz Wesolowski
*/
public class DeclarationGeneratorImpl extends DeclarationGenerator {
private INodeFactory factory;
/**
* Creates a new generator using the given factory.
*
* @param factory The 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) {
IBasicType basicType = (IBasicType) type;
IASTSimpleDeclSpecifier declSpec = factory.newSimpleDeclSpecifier();
declSpec.setType(basicType.getKind());
declSpec.setComplex(basicType.isComplex());
declSpec.setImaginary(basicType.isImaginary());
declSpec.setShort(basicType.isShort());
declSpec.setLong(basicType.isLong());
declSpec.setLongLong(basicType.isLongLong());
declSpec.setSigned(basicType.isSigned());
declSpec.setUnsigned(basicType.isUnsigned());
returnedDeclSpec = declSpec;
} else if (type instanceof ICPPTemplateInstance) {
returnedDeclSpec = getDeclSpecForTemplate((ICPPTemplateInstance) type);
} 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);
}
// TODO(sprigogin): Be honest and return null instead of void.
// Fallback...
if (returnedDeclSpec == null) {
IASTSimpleDeclSpecifier specifier = factory.newSimpleDeclSpecifier();
specifier.setType(Kind.eVoid);
returnedDeclSpec = specifier;
}
return returnedDeclSpec;
}
@Override
public IASTDeclarator createDeclaratorFromType(IType type, char[] name) {
return createDeclaratorFromType(type, name, true);
}
public IASTDeclarator createDeclaratorFromType(IType type, char[] name, boolean changeInitialFunctionToFuncPtr) {
IASTDeclarator returnedDeclarator = null;
try {
// Addition of pointer operators has to be in reverse order, so it's deferred until
// the end.
Map<IASTDeclarator, LinkedList<IASTPointerOperator>> pointerOperatorMap = new HashMap<IASTDeclarator, LinkedList<IASTPointerOperator>>();
IASTName newName = name != null ? factory.newName(name) : factory.newName();
// If the type is an array of something, create a declaration of a pointer to something
// instead (to allow assignment, etc).
boolean replaceInitialArrayWithPointer = true;
// If the type is a function, create a declaration of a pointer to this function
// (shorthand notation for function address).
boolean changeNextFunctionToFuncPtr = changeInitialFunctionToFuncPtr;
while (typeNeedsNontrivialDeclarator(type)) {
if (replaceInitialArrayWithPointer && type instanceof IArrayType) {
returnedDeclarator = factory.newDeclarator(newName);
returnedDeclarator.addPointerOperator(factory.newPointer());
type = ((IArrayType) type).getType();
} else if (changeNextFunctionToFuncPtr && type instanceof IFunctionType) {
returnedDeclarator = factory.newDeclarator(newName);
returnedDeclarator.addPointerOperator(factory.newPointer());
// Leave type as it is, next iteration will handle the function.
} else if (type instanceof IArrayType) {
IArrayType arrayType = (IArrayType) type;
IASTArrayDeclarator arrayDeclarator = factory.newArrayDeclarator(null);
if (returnedDeclarator == null) {
arrayDeclarator.setName(newName);
} else {
arrayDeclarator.setNestedDeclarator(returnedDeclarator);
arrayDeclarator.setName(factory.newName());
}
// Consume all immediately following array expressions.
while (type instanceof IArrayType) {
arrayType = (IArrayType) type;
IASTExpression arraySizeExpression = arrayType.getArraySizeExpression();
arrayDeclarator.addArrayModifier(factory.newArrayModifier(arraySizeExpression == null
? null : arraySizeExpression.copy(CopyStyle.withLocations)));
type = arrayType.getType();
}
returnedDeclarator = arrayDeclarator;
} else if (isPtrOrRefType(type)) {
if (returnedDeclarator == null) {
returnedDeclarator = factory.newDeclarator(newName);
}
IASTPointerOperator ptrOp = createPointerOperator(type);
addPointerOperatorDeferred(pointerOperatorMap, returnedDeclarator, ptrOp);
type = getPtrOrRefSubtype(type);
} else if (type instanceof IFunctionType) {
IFunctionType funcType = (IFunctionType) type;
IASTStandardFunctionDeclarator func = factory.newFunctionDeclarator(null);
for (IType paramType : funcType.getParameterTypes()) {
IASTDeclSpecifier declspec = createDeclSpecFromType(paramType);
IASTDeclarator declarator = null;
if (typeNeedsNontrivialDeclarator(paramType)) {
declarator = createDeclaratorFromType(paramType, null);
} else {
declarator = factory.newDeclarator(factory.newName());
}
IASTParameterDeclaration parameterDeclaration = factory.newParameterDeclaration(
declspec, declarator);
func.addParameterDeclaration(parameterDeclaration);
}
if (returnedDeclarator == null) {
func.setName(newName);
} else {
func.setNestedDeclarator(returnedDeclarator);
func.setName(factory.newName());
}
returnedDeclarator = func;
type = funcType.getReturnType();
}
replaceInitialArrayWithPointer = false;
changeNextFunctionToFuncPtr = false;
}
finalizePointerOperators(pointerOperatorMap);
} catch (DOMException e) {
CCorePlugin.log(e);
}
// Fallback
if (returnedDeclarator == null) {
returnedDeclarator = factory.newDeclarator(factory.newName(name));
}
return returnedDeclarator;
}
private void finalizePointerOperators(
Map<IASTDeclarator, LinkedList<IASTPointerOperator>> pointerOperatorMap) {
for (IASTDeclarator declarator : pointerOperatorMap.keySet()) {
LinkedList<IASTPointerOperator> list = pointerOperatorMap.get(declarator);
for (IASTPointerOperator op : list) {
declarator.addPointerOperator(op);
}
}
}
private void addPointerOperatorDeferred(
Map<IASTDeclarator, LinkedList<IASTPointerOperator>> pointerOperatorMap,
IASTDeclarator returnedDeclarator, IASTPointerOperator ptrOp) {
LinkedList<IASTPointerOperator> list;
if (!pointerOperatorMap.containsKey(returnedDeclarator)) {
list = new LinkedList<>();
pointerOperatorMap.put(returnedDeclarator, list);
} else {
list = pointerOperatorMap.get(returnedDeclarator);
}
list.addFirst(ptrOp);
}
private IType getPtrOrRefSubtype(IType type) {
if (type instanceof IPointerType) {
return ((IPointerType) type).getType();
} else {
return ((ICPPReferenceType) type).getType();
}
}
private IASTPointerOperator createPointerOperator(IType type) {
if (type instanceof ICPPPointerToMemberType) {
String classStr = ASTTypeUtil.getType(((ICPPPointerToMemberType) type).getMemberOfClass());
IASTName newName = factory.newName((classStr + "::").toCharArray()); //$NON-NLS-1$
// Any better way of getting class name from ICPPPointerToMemberType?
ICPPASTPointerToMember member = ((ICPPNodeFactory) factory).newPointerToMember(newName);
member.setConst(((ICPPPointerToMemberType) type).isConst());
member.setVolatile(((ICPPPointerToMemberType) type).isVolatile());
return member;
} else if (type instanceof IPointerType) {
IASTPointer pointer = factory.newPointer();
pointer.setConst(((IPointerType) type).isConst());
pointer.setVolatile(((IPointerType) type).isVolatile());
return pointer;
} else {
ICPPReferenceType refType = (ICPPReferenceType) type;
ICPPASTReferenceOperator op =
((ICPPNodeFactory) factory).newReferenceOperator(refType.isRValueReference());
return op;
}
}
private boolean isPtrOrRefType(IType type) {
return type instanceof IPointerType || type instanceof ICPPReferenceType;
}
private boolean typeNeedsNontrivialDeclarator(IType type) {
return isPtrOrRefType(type) || type instanceof IArrayType || type instanceof IFunctionType;
}
private IASTDeclSpecifier getDeclSpecForTemplate(ICPPTemplateInstance type) {
IASTName name = getName(type);
if (factory instanceof ICPPNodeFactory) {
if (name instanceof ICPPASTQualifiedName) {
ICPPASTQualifiedName fullQualifiedName = (ICPPASTQualifiedName) name;
IASTName templateName = fullQualifiedName.getLastName();
ICPPASTTemplateId tempId = getTemplateId(type, templateName);
ICPPASTQualifiedName newQualifiedName =
((ICPPNodeFactory) factory).newQualifiedName(tempId);
ICPPASTNameSpecifier[] qualifier = fullQualifiedName.getQualifier();
int nbQualifiedNames = qualifier.length;
for (int i = 0; i < nbQualifiedNames; i++) {
newQualifiedName.addNameSpecifier(qualifier[i].copy(CopyStyle.withLocations));
}
return factory.newTypedefNameSpecifier(newQualifiedName);
} else {
IASTName templateName = getName(type);
ICPPASTTemplateId tempId = getTemplateId(type, templateName);
return factory.newTypedefNameSpecifier(tempId);
}
}
return factory.newTypedefNameSpecifier(name);
}
private ICPPASTTemplateId getTemplateId(ICPPTemplateInstance type, IASTName templateName) {
ICPPNodeFactory cppFactory = (ICPPNodeFactory) factory;
ICPPASTTemplateId tempId = cppFactory.newTemplateId(templateName.copy(CopyStyle.withLocations));
for (ICPPTemplateArgument arg : type.getTemplateArguments()) {
IASTTypeId typeId = getTypeIdForTemplateArgument(arg);
tempId.addTemplateArgument(typeId);
}
return tempId;
}
private IASTTypeId getTypeIdForTemplateArgument(ICPPTemplateArgument argument) {
IASTDeclSpecifier argDeclSpec;
IASTDeclarator typeDeclarator = null;
if (argument.isTypeValue()) {
IType argumentType = argument.getTypeValue();
argDeclSpec = createDeclSpecFromType(argumentType);
typeDeclarator = createDeclaratorFromType(argumentType, CharArrayUtils.EMPTY, false);
} else {
argDeclSpec = createDeclSpecFromType(argument.getTypeOfNonTypeValue());
}
IASTTypeId typeId = factory.newTypeId(argDeclSpec, typeDeclarator);
return typeId;
}
private IASTNamedTypeSpecifier getDeclSpecForBinding(IBinding binding) {
IASTName name = getName(binding);
return factory.newTypedefNameSpecifier(name);
}
private IASTName getName(IBinding binding) {
char[][] qualifiedNameCharArray = CPPVisitor.getQualifiedNameCharArray(binding);
IASTName name;
if (qualifiedNameCharArray.length > 1) {
char[] cs = qualifiedNameCharArray[qualifiedNameCharArray.length - 1];
ICPPNodeFactory cppFactory = (ICPPNodeFactory) factory;
ICPPASTName segment = cppFactory.newName(cs);
name = cppFactory.newQualifiedName(segment);
for (int i = 0; i < qualifiedNameCharArray.length - 1; i++) {
segment = cppFactory.newName(cs);
((ICPPASTQualifiedName) name).addNameSpecifier(cppFactory.newName(qualifiedNameCharArray[i]));
}
} else if (qualifiedNameCharArray.length == 1) {
name = factory.newName(qualifiedNameCharArray[0]);
} else {
name = factory.newName(binding.getName().toCharArray());
}
return name;
}
}