blob: 13268a893d13e1bef71fa54487c26c7ec031153c [file] [log] [blame]
package org.eclipse.papyrus.designer.languages.cpp.reverse.reverse
import java.util.logging.Level
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil
import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier
import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition
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.IASTSimpleDeclaration
import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit
import org.eclipse.cdt.core.dom.ast.IBinding
import org.eclipse.cdt.core.dom.ast.IField
import org.eclipse.cdt.core.dom.ast.IProblemBinding
import org.eclipse.cdt.core.dom.ast.ITypedef
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition
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.ICPPASTVisibilityLabel
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding
import org.eclipse.cdt.core.model.ISourceRange
import org.eclipse.cdt.core.model.ISourceReference
import org.eclipse.cdt.core.model.ITranslationUnit
import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPDeferredClassInstance
import org.eclipse.uml2.uml.VisibilityKind
import org.eclipse.uml2.uml.Namespace
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier
import org.eclipse.cdt.core.dom.ast.ASTSignatureUtil
import org.eclipse.cdt.internal.core.model.ASTStringUtil
import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownMemberClass
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization
class ASTUtils {
/**
* Obtain a declSpecifier from a source reference
*/
static def IASTDeclSpecifier getDeclSpecifier(ISourceReference sourceRef) {
return sourceRef.findEnclosingNode.declSpecifier
}
/**
* Obtain a declSpecifier from a node
*/
static def IASTDeclSpecifier getDeclSpecifier(IASTNode node) {
if (node instanceof IASTDeclSpecifier) {
// already a declSpecifier, return it directly
return node;
} else if (node instanceof IASTFunctionDefinition) {
return node.declSpecifier
} else if (node instanceof IASTSimpleDeclaration) {
return node.declSpecifier
} else if (node instanceof ICPPASTTemplateDeclaration) {
// check template declaration recursively
return node.declaration.getDeclSpecifier
}
return null
}
/**
* Obtain a function declaration (typically for operation analysis) from a source reference
*/
static def IASTStandardFunctionDeclarator getDeclarator(ISourceReference sourceRef) {
var node = sourceRef.findEnclosingNode
if (node instanceof ICPPASTFunctionDefinition) {
return node.declarator as IASTStandardFunctionDeclarator
} else if (node instanceof IASTSimpleDeclaration) {
return node.declarators.filter(typeof(IASTStandardFunctionDeclarator)).head
} else if (node instanceof ICPPASTTemplateDeclaration) {
var declaration = node.declaration
if (declaration instanceof IASTSimpleDeclaration) {
return declaration.declarators.filter(typeof(IASTStandardFunctionDeclarator)).head
}
}
return null
}
/**
* get a node-selector from a translation unit. The selector is cached to improve
* performance. It skips already indexed header files
*/
static def getSelector(ITranslationUnit unit) {
var IASTTranslationUnit ast = null
val tuToASTtuMap = ReverseData.current.tuToASTtuMap;
if (unit !== null) {
if (tuToASTtuMap.get(unit) !== null) {
return tuToASTtuMap.get(unit).getNodeSelector(null)
} else {
// the program must already been indexed (i.e. not using CONFIGURE_USING_SOURCE_CONTEXT)
ast = unit.getAST(ReverseData.current.index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS)
if (ast !== null) {
tuToASTtuMap.put(unit, ast)
return ast.getNodeSelector(null);
}
}
}
return null
}
/**
* get the AST node associated with a source reference
*/
static def findEnclosingNode(ISourceReference sourceRef) {
var selector = getSelector(sourceRef.translationUnit)
var ISourceRange range = sourceRef.getSourceRange()
if (selector !== null) {
return selector.findEnclosingNode(range.startPos, range.length)
}
return null
}
/**
* Get a qualified name from a source reference via the AST node
* The function is based on the qualified names in bindings, obtained
* via decl-specifiers or AST names-
* In case of a function definition, the parent binding is used in order
* to obtain the type to which the method belongs.
* If no suitable name is obtained, null is returned.
*/
static def getQualifiedName(ISourceReference sourceRef) {
var node = sourceRef.findEnclosingNode
if (node instanceof IASTName) {
// can obtain name directly
return node.qualifiedName
}
if (node instanceof ICPPASTNamespaceDefinition) {
return node.name.qualifiedName
}
if (node instanceof ICPPASTTemplateDeclaration) {
node = node.declaration
}
if (node instanceof IASTFunctionDefinition) {
// in case of function/method bodies, we're interested in
// in the type owning it. Therefore, navigate to binding
// owner
val binding = node.declarator.name.resolveBinding.owner
if (binding instanceof ICPPBinding) {
return ASTTypeUtil.getQualifiedName(binding)
}
}
// now try to obtain a declSpecifier
val declSpecifier = node.declSpecifier
if (declSpecifier !== null) {
return declSpecifier.qualifiedName
}
return null;
}
/**
* return the qualified name of a declaration based on its
* declaration specifier
*/
static def String getQualifiedName(IASTDeclSpecifier declSpecifier) {
if (declSpecifier instanceof IASTNamedTypeSpecifier) {
return declSpecifier.name.qualifiedName
} else if (declSpecifier instanceof IASTElaboratedTypeSpecifier) {
return declSpecifier.name.qualifiedName
} else if (declSpecifier instanceof IASTCompositeTypeSpecifier) {
return declSpecifier.name.qualifiedName
} else if (declSpecifier instanceof IASTEnumerationSpecifier) {
return declSpecifier.name.qualifiedName
} else {
// LOGGER.log(Level.WARNING, "declSpecifier is not a named type specifier")
// fallback to typename via toString (no ASTName, binding), typically for basic types
return ReverseUtils.getCppTypeName(
ASTStringUtil.getSignatureString(declSpecifier, null))
// return ReverseUtils.getCppTypeName(declSpecifier.toString)
}
}
/**
* CDT resolves bindings completely, i.e. references to "using" names are
* resolved to their right-hand definition
* Therefore recover the first segment
*/
static def String getQNameOfUsing(IASTName name) {
if (name instanceof ICPPASTQualifiedName) {
val segment = name.allSegments.get(0)
if (segment instanceof IASTName) {
val segLastName = segment.lastName
return segLastName.qualifiedName
}
}
return null
}
/**
* get the qualified name from an AST name via a binding.
* If it is a template, get binding of template itself (no arguments)
*/
static def String getQualifiedName(IASTName name) {
val lastName = name.lastName
var IBinding binding
if (lastName instanceof ICPPASTTemplateId) {
binding = lastName.templateName.resolveBinding()
} else {
binding = name.resolveBinding()
}
if (binding instanceof ICPPUnknownMemberClass) {
// remove template signature (in which situations does this make sense?)
// (ICPPUnknownMemberClass represents a binding for a type found in a template
// definition, that can be determined only after the template is instantiated.)
binding = (binding as ICPPUnknownMemberClass).owner
}
return binding.qualifiedName
}
/**
* Get a binding from a C++ name-specifier. If it is a template, get binding
* of template itself (no arguments).
* IASTName is no common supertype
*/
static def getQualifiedNameFromNSpec(ICPPASTNameSpecifier nameSpecifier) {
if (nameSpecifier instanceof ICPPASTTemplateId) {
return nameSpecifier.templateName.resolveBinding.qualifiedName
}
else if (nameSpecifier instanceof IASTName) {
return nameSpecifier.qualifiedName
}
else {
return nameSpecifier.resolveBinding.qualifiedName
}
}
/**
* return a qualified name from a binding from. If the binding cannot
* be resolved, return the name as string
*/
static def String getQualifiedName(IBinding binding) {
if (binding instanceof IProblemBinding) {
ReverseUtils.LOGGER.log(Level.WARNING, String.format("binding <%s> is problematic", binding))
if (binding.candidateBindings.size > 0) {
return binding.candidateBindings.get(0).qualifiedName
}
}
if (binding instanceof IField) {
return binding.compositeTypeOwner.qualifiedName
}
if (binding instanceof ICPPTemplateInstance) {
return (binding.templateDefinition as IBinding).qualifiedName
}
if (binding instanceof ICPPSpecialization) {
// remove template bindings
return (binding.specializedBinding).qualifiedName
}
if (binding instanceof ITypedef) {
//val tst = (binding as ICPPInternalBinding).declarations.get(0)
//if (tst instanceof IASTName) {
// return tst.qualifiedName
//}
}
if (binding instanceof ICPPBinding) {
return ASTTypeUtil.getQualifiedName(binding).stripFNHint
}
else {
// e.g. typedefs + fallback
return binding.name
}
}
/**
* This functions removes a file-name hint that is eventually present in
* a qualified name. The ASTTypeUtil adds it in enclosed with { .. } to detect
* recursively calls to the same binding.
*/
static def String stripFNHint(String qName) {
if (qName !== null) {
var idx = qName.lastIndexOf("{")
if (idx != -1) {
if (idx > 2) idx-=2; // strip preceding "::", if not first entry
var endIdx = qName.indexOf("}", idx)
if (endIdx != -1) {
// strip preceding "::", if not last entry
if (endIdx < qName.length-2) endIdx+=2;
val strippedName = qName.substring(0, idx) + qName.substring(endIdx + 1)
return strippedName.stripFNHint
}
}
}
return qName
}
/**
* get the target translation unit from a name
*/
static def getTargetTU(IASTName name) {
return ReverseUtils.getTranslationUnitFromPath(name.containingFilename, ReverseData.current.project)
}
/**
* return the qualified name of a template type
*/
static def getTemplateType(IASTName astName, String name) {
var IASTNode parent = astName
while (parent !== null) {
if (parent instanceof ICPPASTTemplateDeclaration) {
for (parameter : parent.templateParameters) {
if (parameter.toString == name) {
val templateQN = (parent.declaration as IASTSimpleDeclaration).declSpecifier.qualifiedName
return templateQN + "::" + name
}
}
}
parent = parent.parent
}
return name
}
/**
* Get the C++ typename from an AST decl specifier
* This includes all modifiers and eventual template parameters
*/
static def String getCppTypeName(IASTDeclSpecifier declarator) {
var String parameterTypeName = ""; // $NON-NLS-1$
try {
var token = declarator.syntax
while (token !== null) {
var String tokenStr = token.toString()
if (tokenStr.equals("*")) { // $NON-NLS-1$
// TODO: check, if this can be called (depending on
// * position with different semantics?)
// isPointer = true;
} else if (tokenStr.equals("&")) { // $NON-NLS-1$
// isRef = true;
} else if (tokenStr.equals("const")) { // $NON-NLS-1$
// do nothing (use isConst() operation of
// parameterType)
// is not part of parameter type
} else {
if (parameterTypeName.length() > 0) {
parameterTypeName += " "; // $NON-NLS-1$
}
parameterTypeName += tokenStr;
}
token = token.getNext();
}
} catch (ExpansionOverlapsBoundaryException e) {
}
return parameterTypeName
}
/**
* Convert AST visibilities ...
*/
static def convertVisibility(ASTAccessVisibility visibility) {
if(visibility == ASTAccessVisibility.PRIVATE) return VisibilityKind.PRIVATE_LITERAL
if(visibility == ASTAccessVisibility.PROTECTED) return VisibilityKind.PROTECTED_LITERAL
return VisibilityKind.PUBLIC_LITERAL
}
/**
* Yes, there are two AST ways for visibilities ...
*/
static def convertVisibility(int visibility) {
if(visibility == ICPPASTVisibilityLabel.v_private) return VisibilityKind.PRIVATE_LITERAL
if(visibility == ICPPASTVisibilityLabel.v_protected) return VisibilityKind.PROTECTED_LITERAL
return VisibilityKind.PUBLIC_LITERAL
}
}