/*******************************************************************************
* Copyright (c) 2009,2010 Alena Laskavaia
* 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:
* Alena Laskavaia - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.codan.internal.checkers;
import java.util.Iterator;
import org.eclipse.cdt.codan.core.cxx.model.AbstractAstFunctionChecker;
import org.eclipse.cdt.codan.core.cxx.model.CxxModelsCache;
import org.eclipse.cdt.codan.core.model.IProblem;
import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy;
import org.eclipse.cdt.codan.core.model.cfg.ICfgData;
import org.eclipse.cdt.codan.core.model.cfg.IControlFlowGraph;
import org.eclipse.cdt.codan.core.model.cfg.IExitNode;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.c.ICASTSimpleDeclSpecifier;
/**
* The checker suppose to find issue related to mismatched return value/function
* declaration
*
Function declared as returning non-void returns void
* Function declared as returning void has non-void return
* Function declared as returning non-void has no return (requires control
* flow graph)
*/
public class ReturnChecker extends AbstractAstFunctionChecker {
private static final String PARAM_IMPLICIT = "implicit"; //$NON-NLS-1$
public final String RET_NO_VALUE_ID = "org.eclipse.cdt.codan.checkers.noreturn"; //$NON-NLS-1$
public final String RET_ERR_VALUE_ID = "org.eclipse.cdt.codan.checkers.errreturnvalue"; //$NON-NLS-1$
public final String RET_NORET_ID = "org.eclipse.cdt.codan.checkers.errnoreturn"; //$NON-NLS-1$
class ReturnStmpVisitor extends ASTVisitor {
private IASTFunctionDefinition func;
boolean hasret;
ReturnStmpVisitor(IASTFunctionDefinition func) {
shouldVisitStatements = true;
shouldVisitDeclarations = true;
this.func = func;
this.hasret = false;
}
public int visit(IASTDeclaration element) {
if (element!=func)
return PROCESS_SKIP; // skip inner functions
return PROCESS_CONTINUE;
}
public int visit(IASTStatement stmt) {
if (stmt instanceof IASTReturnStatement) {
hasret = true;
IASTReturnStatement ret = (IASTReturnStatement) stmt;
if (!isVoid(func)) {
if (checkImplicitReturn(RET_NO_VALUE_ID)
|| isExplicitReturn(func)) {
if (ret.getReturnValue() == null)
reportProblem(RET_NO_VALUE_ID, ret);
}
} else {
if (ret.getReturnValue() != null) {
IType type = ret.getReturnValue().getExpressionType();
if (isVoid(type))
return PROCESS_SKIP;
reportProblem(RET_ERR_VALUE_ID, ret.getReturnValue());
}
}
return PROCESS_SKIP;
}
return PROCESS_CONTINUE;
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.cdt.codan.core.cxx.model.AbstractAstFunctionChecker#
* processFunction(org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition)
*/
@Override
protected void processFunction(IASTFunctionDefinition func) {
ReturnStmpVisitor visitor = new ReturnStmpVisitor(func);
func.accept(visitor);
if (!visitor.hasret) {
// no return at all
if (!isVoid(func)
&& (checkImplicitReturn(RET_NORET_ID) || isExplicitReturn(func))) {
if (endsWithNoExitNode(func))
reportProblem(RET_NORET_ID, func.getDeclSpecifier());
}
}
}
/**
* @param if - problem id
* @return true if need to check inside functions with implicit return
*/
protected boolean checkImplicitReturn(String id) {
final IProblem pt = getProblemById(id, getFile());
return (Boolean) getPreference(pt,PARAM_IMPLICIT);
}
/**
* @param func
* @return
*/
protected boolean endsWithNoExitNode(IASTFunctionDefinition func) {
IControlFlowGraph graph = CxxModelsCache.getInstance()
.getControlFlowGraph(func);
Iterator exitNodeIterator = graph.getExitNodeIterator();
boolean noexitop = false;
for (; exitNodeIterator.hasNext();) {
IExitNode node = exitNodeIterator.next();
if (((ICfgData) node).getData() == null) {
// if it real exit node such as return, exit or throw data
// will be an ast node, it is null it is fake node added by the
// graph builder
noexitop = true;
break;
}
}
return noexitop;
}
/**
* @param func
* @return
*/
protected boolean isExplicitReturn(IASTFunctionDefinition func) {
return getDeclSpecType(func) != ICASTSimpleDeclSpecifier.t_unspecified;
}
/**
* @param func
* @return
*/
public boolean isVoid(IASTFunctionDefinition func) {
int type = getDeclSpecType(func);
if (type == IASTSimpleDeclSpecifier.t_void) {
IASTFunctionDeclarator declarator = func.getDeclarator();
if (declarator.getPointerOperators().length == 0)
return true;
}
return false;
}
/**
* check if type if void
* (uses deprecated API for compatibility with 6.0)
* @param type
* @throws DOMException
*/
@SuppressWarnings("deprecation")
public boolean isVoid(IType type) {
if (type instanceof IBasicType) {
try {
if (((IBasicType) type).getType()==IBasicType.t_void)
return true;
} catch (DOMException e) {
return false;
}
}
return false;
}
/**
* @param func
* @return
*/
protected int getDeclSpecType(IASTFunctionDefinition func) {
IASTDeclSpecifier declSpecifier = func.getDeclSpecifier();
int type = -1;
if (declSpecifier instanceof IASTSimpleDeclSpecifier) {
type = ((IASTSimpleDeclSpecifier) declSpecifier).getType();
}
return type;
}
/* checker must implement @link ICheckerWithPreferences */
public void initPreferences(IProblemWorkingCopy problem) {
super.initPreferences(problem);
if (problem.getId().equals(RET_NO_VALUE_ID)
|| problem.getId().equals(RET_NORET_ID)) {
addPreference(problem, PARAM_IMPLICIT,
CheckersMessages.ReturnChecker_Param0, Boolean.FALSE);
}
}
}