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:
parent
52d90b84b2
commit
bba580e5a0
1 changed files with 305 additions and 30 deletions
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue