1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-23 17:05:26 +02:00

Bug 540112 - Perform C++14 return type deduction in ReturnChecker

As part of this change, ReturnChecker was refactored to compute the
return type as an IType, which allowed for removal of some logic in
ReturnChecker which duplicated CPPVisitor's type resolution work.

Change-Id: I9cd8512164d650a5ee11d2e58fdae477e3c428a2
This commit is contained in:
Nathan Ridge 2018-10-15 02:01:01 -04:00
parent 4689fdee68
commit a00346af22
5 changed files with 65 additions and 80 deletions

View file

@ -25,33 +25,26 @@ import org.eclipse.cdt.codan.core.model.cfg.IControlFlowGraph;
import org.eclipse.cdt.codan.core.model.cfg.IExitNode;
import org.eclipse.cdt.codan.internal.core.cfg.ControlFlowGraph;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
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.IASTDoStatement;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTForStatement;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTIfStatement;
import org.eclipse.cdt.core.dom.ast.IASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.IASTLabelStatement;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
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.IASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.dom.ast.IASTWhileStatement;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLambdaExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTryBlockStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
@ -70,6 +63,8 @@ public class ReturnChecker extends AbstractAstFunctionChecker {
public static final String RET_NO_VALUE_ID = "org.eclipse.cdt.codan.checkers.noreturn"; //$NON-NLS-1$
public static final String RET_ERR_VALUE_ID = "org.eclipse.cdt.codan.checkers.errreturnvalue"; //$NON-NLS-1$
public static final String RET_NORET_ID = "org.eclipse.cdt.codan.checkers.errnoreturn"; //$NON-NLS-1$
private IType cachedReturnType = null;
class ReturnStmpVisitor extends ASTVisitor {
private final IASTFunctionDefinition func;
@ -149,6 +144,7 @@ public class ReturnChecker extends AbstractAstFunctionChecker {
@Override
protected void processFunction(IASTFunctionDefinition func) {
cachedReturnType = null;
if (func.getParent() instanceof ICPPASTTemplateDeclaration)
return; // If it is template get out of here.
ReturnStmpVisitor visitor = new ReturnStmpVisitor(func);
@ -190,6 +186,8 @@ public class ReturnChecker extends AbstractAstFunctionChecker {
return false;
}
@SuppressWarnings("restriction")
// TODO: Any reason not to just expose getDeadNodes() in IControlFlowGraph?
public Collection<IBasicBlock> getDeadBlocks(IASTFunctionDefinition func) {
IControlFlowGraph graph = getModelCache().getControlFlowGraph(func);
return ((ControlFlowGraph) graph).getDeadNodes();
@ -245,7 +243,9 @@ public class ReturnChecker extends AbstractAstFunctionChecker {
}
protected boolean isExplicitReturn(IASTFunctionDefinition func) {
return getDeclSpecType(func) != IASTSimpleDeclSpecifier.t_unspecified;
IASTDeclSpecifier declSpecifier = func.getDeclSpecifier();
return !(declSpecifier instanceof IASTSimpleDeclSpecifier &&
((IASTSimpleDeclSpecifier) declSpecifier).getType() == IASTSimpleDeclSpecifier.t_unspecified);
}
/**
@ -258,41 +258,21 @@ public class ReturnChecker extends AbstractAstFunctionChecker {
private boolean isNonVoid(IASTFunctionDefinition func) {
if (isConstructorDestructor(func))
return false;
int type = getDeclSpecType(func);
if (type == IASTSimpleDeclSpecifier.t_void) {
IASTFunctionDeclarator declarator = func.getDeclarator();
if (declarator.getPointerOperators().length == 0)
return false;
} else if (type == IASTSimpleDeclSpecifier.t_auto && isAutoVoid(func)) {
return false;
return !isVoid(getReturnType(func));
}
private IType getReturnType(IASTFunctionDefinition func) {
if (cachedReturnType == null) {
cachedReturnType = CxxAstUtils.getReturnType(func);
}
return true;
return cachedReturnType;
}
/**
* Checks if type if void.
*
* @param type
* @throws DOMException
*/
public boolean isVoid(IType type) {
return type instanceof IBasicType && ((IBasicType) type).getKind() == IBasicType.Kind.eVoid;
}
protected int getDeclSpecType(IASTFunctionDefinition func) {
IASTDeclSpecifier declSpecifier = func.getDeclSpecifier();
int type = -1;
if (declSpecifier instanceof IASTSimpleDeclSpecifier) {
type = ((IASTSimpleDeclSpecifier) declSpecifier).getType();
} else if (declSpecifier instanceof IASTNamedTypeSpecifier) {
IBinding binding = ((IASTNamedTypeSpecifier) declSpecifier).getName().resolveBinding();
IType utype = CxxAstUtils.unwindTypedef((IType) binding);
if (isVoid(utype))
return IASTSimpleDeclSpecifier.t_void;
}
return type;
}
@Override
public void initPreferences(IProblemWorkingCopy problem) {
super.initPreferences(problem);
@ -300,38 +280,4 @@ public class ReturnChecker extends AbstractAstFunctionChecker {
addPreference(problem, PARAM_IMPLICIT, CheckersMessages.ReturnChecker_Param0, Boolean.FALSE);
}
}
/**
* Checks if a {@code void} return type is specified using the C++11 late-specified return type
* for a given function definition.
* <p>
* For example, <code>auto f() -> void {}</code> would return {@code true}.
*
* @param functionDefinition
* @return {@code true} if the function has a void (late-specified) return type,
* {@code false} otherwise
*/
private boolean isAutoVoid(IASTFunctionDefinition functionDefinition) {
IASTFunctionDeclarator declarator = functionDefinition.getDeclarator();
if (declarator instanceof ICPPASTFunctionDeclarator) {
ICPPASTFunctionDeclarator functionDeclarator = (ICPPASTFunctionDeclarator) declarator;
IASTTypeId trailingReturnType = functionDeclarator.getTrailingReturnType();
if (trailingReturnType != null) {
IASTDeclarator abstractDeclarator = trailingReturnType.getAbstractDeclarator();
if (abstractDeclarator != null) {
if (abstractDeclarator.getPointerOperators().length != 0) {
return false;
}
IASTDeclSpecifier declSpecifier = trailingReturnType.getDeclSpecifier();
if (declSpecifier instanceof ICPPASTSimpleDeclSpecifier) {
ICPPASTSimpleDeclSpecifier simpleDeclSpecifier = (ICPPASTSimpleDeclSpecifier) declSpecifier;
if (simpleDeclSpecifier.getType() == IASTSimpleDeclSpecifier.t_void) {
return true;
}
}
}
}
}
return false;
}
}

View file

@ -50,6 +50,7 @@ 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.cpp.ICPPASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.rewrite.DeclarationGenerator;
import org.eclipse.cdt.core.index.IIndex;
@ -57,6 +58,8 @@ import org.eclipse.cdt.core.index.IIndexFile;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.model.CoreModelUtil;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.core.dom.parser.c.CVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.core.runtime.CoreException;
@ -422,4 +425,16 @@ public final class CxxAstUtils {
}
return functionNameExpression.getRawSignature().equals("exit"); //$NON-NLS-1$
}
@SuppressWarnings("restriction")
public static IType getReturnType(IASTFunctionDefinition func) {
// We could do this with public API (func.getDeclarator().getName().resolveBinding().getType()
// .getReturnType()), but that would trigger resolution of the parameter types as well,
// which is needless extra work.
if (func instanceof ICPPASTFunctionDefinition) {
return CPPVisitor.createType(func.getDeclarator(),
CPPVisitor.RESOLVE_PLACEHOLDERS | CPPVisitor.ONLY_RETURN_TYPE);
}
return CVisitor.createType(func.getDeclarator(), CVisitor.ONLY_RETURN_TYPE);
}
}

View file

@ -453,4 +453,9 @@ public class ReturnCheckerTest extends CheckerTestCase {
public void testDoubleSemicolonInSwitchCase_455828() throws Exception {
checkSampleAboveCpp();
}
// auto f() {}
public void testReturnTypeDeduction_540112() throws Exception {
checkSampleAboveCpp();
}
}

View file

@ -120,6 +120,12 @@ import org.eclipse.cdt.internal.core.parser.util.ContentAssistMatcherFactory;
public class CVisitor extends ASTQueries {
private static final CBasicType UNSIGNED_LONG_INT = new CBasicType(Kind.eInt, IBasicType.IS_LONG | IBasicType.IS_UNSIGNED);
// Flags for createType().
// Given a function declarator, compute only the return type rather than
// the entire function type.
public static final int ONLY_RETURN_TYPE = 0x1;
public static class CollectProblemsAction extends ASTVisitor {
{
shouldVisitDeclarations = true;
@ -1217,6 +1223,9 @@ public class CVisitor extends ASTQueries {
* @return the IType of the IASTDeclarator parameter
*/
public static IType createType(IASTDeclarator declarator) {
return createType(declarator, 0);
}
public static IType createType(IASTDeclarator declarator, int flags) {
IASTDeclSpecifier declSpec = null;
IASTNode node = declarator.getParent();
@ -1238,7 +1247,7 @@ public class CVisitor extends ASTQueries {
boolean isParameter = (node instanceof IASTParameterDeclaration || node.getParent() instanceof ICASTKnRFunctionDeclarator);
IType type = createType((ICASTDeclSpecifier) declSpec);
type = createType(type, declarator);
type = createType(type, declarator, flags);
if (isParameter) {
IType paramType = type;
@ -1271,8 +1280,8 @@ public class CVisitor extends ASTQueries {
return createType(typeId.getAbstractDeclarator());
}
public static IType createType(IType baseType, IASTDeclarator declarator) {
if (declarator instanceof IASTFunctionDeclarator)
public static IType createType(IType baseType, IASTDeclarator declarator, int flags) {
if (((flags & ONLY_RETURN_TYPE) == 0) && declarator instanceof IASTFunctionDeclarator)
return createType(baseType, (IASTFunctionDeclarator) declarator);
IType type = baseType;
@ -1282,7 +1291,7 @@ public class CVisitor extends ASTQueries {
IASTDeclarator nested = declarator.getNestedDeclarator();
if (nested != null) {
return createType(type, nested);
return createType(type, nested, flags);
}
return type;
}
@ -1340,7 +1349,7 @@ public class CVisitor extends ASTQueries {
IASTDeclarator nested = declarator.getNestedDeclarator();
if (nested != null) {
return createType(type, nested);
return createType(type, nested, 0);
}
return type;
}

View file

@ -249,8 +249,14 @@ public class CPPVisitor extends ASTQueries {
public static final IASTInitializerClause[] NO_ARGS = {};
// Flags for createType().
// Attempt to resolve placeholders ('auto' and 'decltype(auto)').
public static final int RESOLVE_PLACEHOLDERS = 0x1;
// Given a function declarator, compute only the return type rather than
// the entire function type.
public static final int ONLY_RETURN_TYPE = 0x2;
// Common combinations of flags.
public static final int DO_NOT_RESOLVE_PLACEHOLDERS = 0;
@ -2149,7 +2155,7 @@ public class CPPVisitor extends ASTQueries {
IType type = createType(declSpec);
type = makeConstIfConstexpr(type, declSpec, declarator);
type = createType(type, declarator);
type = createType(type, declarator, flags);
// C++ specification 8.3.4.3 and 8.5.1.4
IASTNode initClause= declarator.getInitializer();
@ -2537,6 +2543,10 @@ public class CPPVisitor extends ASTQueries {
}
if (returnType != null) {
if ((flags & ONLY_RETURN_TYPE) != 0) {
return returnType;
}
// Do not use createFunctionType() because that would decorate the return type
// with pointer operators from e.g. an 'auto &', but we have already done that
// above.
@ -2601,7 +2611,7 @@ public class CPPVisitor extends ASTQueries {
type = qualifyType(type, declSpec);
type = makeConstIfConstexpr(type, declSpec, declarator);
// Ignore function declarator because we already handled that in createAutoFunctionType().
return createType(type, declarator, true /* ignore function declarator */);
return createType(type, declarator, ONLY_RETURN_TYPE);
}
private static IType makeConstIfConstexpr(IType type, IASTDeclSpecifier declSpec, IASTDeclarator declarator) {
@ -2621,11 +2631,11 @@ public class CPPVisitor extends ASTQueries {
}
private static IType createType(IType baseType, IASTDeclarator declarator) {
return createType(baseType, declarator, false);
return createType(baseType, declarator, 0);
}
private static IType createType(IType baseType, IASTDeclarator declarator, boolean ignoreFunctionDeclarator) {
if (!ignoreFunctionDeclarator && declarator instanceof ICPPASTFunctionDeclarator)
private static IType createType(IType baseType, IASTDeclarator declarator, int flags) {
if (((flags & ONLY_RETURN_TYPE) == 0) && declarator instanceof ICPPASTFunctionDeclarator)
return createFunctionType(baseType, (ICPPASTFunctionDeclarator) declarator);
IType type = baseType;