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;