From a00346af224c6a60a70151908790272d54d58955 Mon Sep 17 00:00:00 2001 From: Nathan Ridge Date: Mon, 15 Oct 2018 02:01:01 -0400 Subject: [PATCH] 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 --- .../internal/checkers/ReturnChecker.java | 86 ++++--------------- .../cdt/codan/core/cxx/CxxAstUtils.java | 15 ++++ .../internal/checkers/ReturnCheckerTest.java | 5 ++ .../internal/core/dom/parser/c/CVisitor.java | 19 ++-- .../dom/parser/cpp/semantics/CPPVisitor.java | 20 +++-- 5 files changed, 65 insertions(+), 80 deletions(-) diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/ReturnChecker.java b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/ReturnChecker.java index 8b23febac16..a2b3389233d 100644 --- a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/ReturnChecker.java +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/ReturnChecker.java @@ -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 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. - *

- * For example, auto f() -> void {} 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; - } } diff --git a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/CxxAstUtils.java b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/CxxAstUtils.java index ff9f1096118..7f992fc8858 100644 --- a/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/CxxAstUtils.java +++ b/codan/org.eclipse.cdt.codan.core.cxx/src/org/eclipse/cdt/codan/core/cxx/CxxAstUtils.java @@ -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); + } } diff --git a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/ReturnCheckerTest.java b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/ReturnCheckerTest.java index bc42a2cbf2b..02adada820d 100644 --- a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/ReturnCheckerTest.java +++ b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/ReturnCheckerTest.java @@ -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(); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/CVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/CVisitor.java index 0c9a244d67c..8e5d92a6164 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/CVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/CVisitor.java @@ -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; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java index f71a7bb68df..a0590edfb76 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java @@ -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;