1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 14:42:11 +02:00

Bug 319196 - updated utils with more useful methods (contributed by Tomasz Wesolowski)

This commit is contained in:
Alena Laskavaia 2010-07-21 00:03:17 +00:00
parent 52d90b84b2
commit bba580e5a0

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2009, 2010 Alena Laskavaia, Tomasz Wesolowksi
* Copyright (c) 2009, 2010 Alena Laskavaia, 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
@ -11,34 +11,69 @@
*******************************************************************************/
package org.eclipse.cdt.codan.core.cxx;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
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.rewrite.DeclarationGenerator;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
/**
* Useful functions for doing code analysis on c/c++ AST
*/
public final class CxxAstUtils {
public static class NameFinderVisitor extends ASTVisitor {
public IASTName name;
{
shouldVisitNames = true;
}
@Override
public int visit(IASTName name) {
this.name = name;
return PROCESS_ABORT;
}
}
private static CxxAstUtils instance;
private CxxAstUtils() {
@ -74,7 +109,7 @@ public final class CxxAstUtils {
IASTNodeSelector nodeSelector = node.getTranslationUnit()
.getNodeSelector(node.getTranslationUnit().getFilePath());
IASTFileLocation fileLocation = node.getFileLocation();
IASTPreprocessorMacroExpansion macro = nodeSelector
.findEnclosingMacroExpansion(fileLocation.getNodeOffset(),
fileLocation.getNodeLength());
@ -88,7 +123,8 @@ public final class CxxAstUtils {
return (IASTFunctionDefinition) node;
}
public IASTCompositeTypeSpecifier getEnclosingCompositeTypeSpecifier(IASTNode node) {
public IASTCompositeTypeSpecifier getEnclosingCompositeTypeSpecifier(
IASTNode node) {
while (node != null && !(node instanceof IASTCompositeTypeSpecifier)) {
node = node.getParent();
}
@ -103,41 +139,280 @@ public final class CxxAstUtils {
}
/**
* @param astName a name for the declaration
* @param factory the factory
* @param astName
* a name for the declaration
* @param factory
* the factory
* @return
*/
public IASTDeclaration createDeclaration(IASTName astName, INodeFactory factory) {
IASTSimpleDeclaration declaration = factory.newSimpleDeclaration(null);
IASTDeclarator declarator = factory.newDeclarator(astName.copy());
IASTDeclSpecifier declspec = factory.newSimpleDeclSpecifier();
((IASTSimpleDeclSpecifier)declspec).setType(Kind.eVoid);
public IASTDeclaration createDeclaration(IASTName astName,
INodeFactory factory) {
// Depending on context, either a type or a declaration is easier to
// infer
IType inferredType = null;
IASTSimpleDeclaration declaration = null;
inferredType = tryInferTypeFromBinaryExpr(astName);
if (inferredType == null)
declaration = tryInferTypeFromFunctionCall(astName, factory);
// After the inference, create the statement is needed
if (declaration != null) { // A declaration was generated
return declaration;
} else if (inferredType != null) { // A type was inferred, create the
// declaration and return it
DeclarationGenerator generator = DeclarationGenerator
.create(factory);
IASTDeclarator declarator = generator.createDeclaratorFromType(
inferredType, astName.toCharArray());
IASTDeclSpecifier declspec = generator
.createDeclSpecFromType(inferredType);
IASTSimpleDeclaration simpleDeclaration = factory
.newSimpleDeclaration(declspec);
simpleDeclaration.addDeclarator(declarator);
return simpleDeclaration;
} else { // Fallback - return a `void` declaration
IASTDeclarator declarator = factory.newDeclarator(astName.copy());
IASTSimpleDeclSpecifier declspec = factory.newSimpleDeclSpecifier();
declspec.setType(Kind.eVoid);
IASTSimpleDeclaration simpleDeclaration = factory
.newSimpleDeclaration(declspec);
simpleDeclaration.addDeclarator(declarator);
return simpleDeclaration;
}
}
/**
* For any BinaryExpression, guess the type from the other operand. (A good
* guess for =, ==; hard to get a better guess for others)
*
* @return inferred type or null if couldn't infer
*/
private IType tryInferTypeFromBinaryExpr(IASTName astName) {
if (astName.getParent() instanceof IASTIdExpression
&& astName.getParent().getParent() instanceof IASTBinaryExpression
&& ((IASTBinaryExpression) astName.getParent().getParent())
.getOperator() == IASTBinaryExpression.op_assign
&& astName.getParent().getParent().getParent() instanceof IASTExpressionStatement) {
&& astName.getParent().getParent() instanceof IASTBinaryExpression) {
IASTNode binaryExpr = astName.getParent().getParent();
IASTExpression tgt = null;
for (IASTNode node : binaryExpr.getChildren()) {
if (node != astName.getParent()) {
// use this expression as type source
tgt = (IASTExpression) node;
break;
return ((IASTExpression) node).getExpressionType();
}
}
if (tgt != null) {
DeclarationGenerator generator = DeclarationGenerator.create(factory);
IType type = tgt.getExpressionType();
declarator = generator.createDeclaratorFromType(type, astName.toCharArray());
declspec = generator.createDeclSpecFromType(type);
}
}
declaration.setDeclSpecifier(declspec);
declaration.addDeclarator(declarator);
return declaration;
return null;
}
/**
* For a function call, tries to find a matching function declaration.
* Checks the argument count.
*
* @return a generated declaration or null if not suitable
*/
private IASTSimpleDeclaration tryInferTypeFromFunctionCall(
IASTName astName, INodeFactory factory) {
if (astName.getParent() instanceof IASTIdExpression
&& astName.getParent().getParent() instanceof IASTFunctionCallExpression
&& astName.getParent().getPropertyInParent() == IASTFunctionCallExpression.ARGUMENT) {
IASTFunctionCallExpression call = (IASTFunctionCallExpression) astName
.getParent().getParent();
NameFinderVisitor visitor = new NameFinderVisitor();
call.getFunctionNameExpression().accept(visitor);
IASTName funcname = visitor.name;
int expectedParametersNum = 0;
int targetParameterNum = -1;
for (IASTNode n : call.getChildren()) {
if (n.getPropertyInParent() == IASTFunctionCallExpression.ARGUMENT) {
if (n instanceof IASTIdExpression
&& n.getChildren()[0] == astName) {
targetParameterNum = expectedParametersNum;
}
expectedParametersNum++;
}
}
if (targetParameterNum == -1) {
return null;
}
IIndex index = astName.getTranslationUnit().getIndex();
if (index == null) {
return null;
}
IBinding[] bindings;
{
IBinding binding = funcname.resolveBinding();
if (binding instanceof IProblemBinding) {
bindings = ((IProblemBinding) binding)
.getCandidateBindings();
} else {
bindings = new IBinding[] { binding };
}
}
try {
index.acquireReadLock();
Set<IIndexName> declSet = new HashSet<IIndexName>();
// fill declSet with proper declarations
for (IBinding b : bindings) {
if (b instanceof IFunction) {
IFunction f = (IFunction) b;
try {
if (f.getParameters().length == expectedParametersNum) {
// Consider this overload
IIndexName[] decls = index.findDeclarations(b);
declSet.addAll(Arrays.asList(decls));
}
} catch (DOMException e) {
continue;
}
}
}
for (IIndexName decl : declSet) {
// for now, just use the first overload found
ITranslationUnit tu = getTranslationUnitFromIndexName(decl);
IASTName name = (IASTName) tu.getAST(null,
ITranslationUnit.AST_SKIP_INDEXED_HEADERS)
.getNodeSelector(null).findEnclosingNode(
decl.getNodeOffset(), decl.getNodeLength());
IASTNode fdecl = name;
while (fdecl instanceof IASTName) {
fdecl = fdecl.getParent();
}
assert (fdecl instanceof IASTFunctionDeclarator);
// find the needed param number
int nthParam = 0;
for (IASTNode child : fdecl.getChildren()) {
if (child instanceof IASTParameterDeclaration) {
if (nthParam == targetParameterNum) {
IASTParameterDeclaration pd = (IASTParameterDeclaration) child;
IASTDeclSpecifier declspec = pd
.getDeclSpecifier().copy();
IASTDeclarator declarator = pd.getDeclarator()
.copy();
setNameInNestedDeclarator(declarator, astName
.copy());
IASTSimpleDeclaration declaration = factory
.newSimpleDeclaration(declspec);
declaration.addDeclarator(declarator);
return declaration;
}
nthParam++;
}
}
name.getParent();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (CoreException e) {
e.printStackTrace();
} finally {
index.releaseReadLock();
}
}
return null;
}
private void setNameInNestedDeclarator(IASTDeclarator declarator,
IASTName astName) {
while (declarator.getNestedDeclarator() != null) {
declarator = declarator.getNestedDeclarator();
}
declarator.setName(astName);
}
public ITranslationUnit getTranslationUnitFromIndexName(IIndexName decl)
throws CoreException {
Path path = new Path(decl.getFile().getLocation().getFullPath());
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
ITranslationUnit tu = (ITranslationUnit) CoreModel.getDefault().create(
file);
return tu;
}
/**
* If the function definition belongs to a class, returns the class.
* Otherwise, returns null.
*
* @param function
* the function definition to check
* @param index
* the index to use for name lookup
* @return Either a type specifier or null
*/
public IASTCompositeTypeSpecifier getCompositeTypeFromFunction(
final IASTFunctionDefinition function, final IIndex index) {
// return value to be set via visitor
final IASTCompositeTypeSpecifier returnSpecifier[] = { null };
function.accept(new ASTVisitor() {
{
shouldVisitDeclarators = true;
shouldVisitNames = true;
}
@Override
public int visit(IASTName name) {
if (!(name instanceof ICPPASTQualifiedName && name.getParent()
.getParent() == function))
return PROCESS_CONTINUE;
ICPPASTQualifiedName qname = (ICPPASTQualifiedName) name;
// A qualified name may have 1 name, but in our case needs to
// have 2.
// The pre-last name is either a namespace or a class.
if (qname.getChildren().length < 2) {
return PROCESS_CONTINUE;
}
IASTName namePart = (IASTName) qname.getChildren()[qname
.getChildren().length - 2];
IBinding binding = namePart.resolveBinding();
try {
index.acquireReadLock();
IIndexName[] declarations = index.findDeclarations(binding);
// Check the declarations and use first suitable
for (IIndexName decl : declarations) {
ITranslationUnit tu = getTranslationUnitFromIndexName(decl);
IASTNode node = tu.getAST(null,
ITranslationUnit.AST_SKIP_INDEXED_HEADERS)
.getNodeSelector(null).findEnclosingNode(
decl.getNodeOffset(),
decl.getNodeLength());
IASTCompositeTypeSpecifier specifier = getEnclosingCompositeTypeSpecifier(node);
if (specifier != null) {
returnSpecifier[0] = specifier;
break;
}
}
} catch (InterruptedException e) {
return PROCESS_ABORT;
} catch (CoreException e) {
e.printStackTrace();
return PROCESS_ABORT;
} finally {
index.releaseReadLock();
}
return PROCESS_ABORT;
}
});
return returnSpecifier[0];
}
}