diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/AssignExpr.java b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/AssignExpr.java index df1cd8bb78..9cd102c4f1 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/AssignExpr.java +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/AssignExpr.java @@ -20,20 +20,22 @@ */ package com.github.javaparser.ast.expr; +import static com.github.javaparser.utils.Utils.assertNotNull; + +import java.util.Optional; +import java.util.function.Consumer; + +import com.github.javaparser.TokenRange; import com.github.javaparser.ast.AllFieldsConstructor; +import com.github.javaparser.ast.Generated; +import com.github.javaparser.ast.Node; import com.github.javaparser.ast.observer.ObservableProperty; +import com.github.javaparser.ast.visitor.CloneVisitor; import com.github.javaparser.ast.visitor.GenericVisitor; import com.github.javaparser.ast.visitor.VoidVisitor; -import static com.github.javaparser.utils.Utils.assertNotNull; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.visitor.CloneVisitor; import com.github.javaparser.metamodel.AssignExprMetaModel; import com.github.javaparser.metamodel.JavaParserMetaModel; import com.github.javaparser.printer.Stringable; -import com.github.javaparser.TokenRange; -import java.util.function.Consumer; -import java.util.Optional; -import com.github.javaparser.ast.Generated; /** * An assignment expression. It supports the operators that are found the the AssignExpr.Operator enum. @@ -253,4 +255,14 @@ public void ifAssignExpr(Consumer action) { public Optional toAssignExpr() { return Optional.of(this); } + + /* + * Returns true if the expression is an assignment context + * https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.2 + * 5.2. Assignment Contexts: Assignment contexts allow the value of an expression to be assigned (§15.26) to a variable;... + */ + @Override + protected boolean isAssignmentContext() { + return true; + } } diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/ConditionalExpr.java b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/ConditionalExpr.java index 555502126b..072d445356 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/ConditionalExpr.java +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/ConditionalExpr.java @@ -20,20 +20,22 @@ */ package com.github.javaparser.ast.expr; +import static com.github.javaparser.utils.Utils.assertNotNull; + +import java.util.Optional; +import java.util.function.Consumer; + +import com.github.javaparser.TokenRange; import com.github.javaparser.ast.AllFieldsConstructor; +import com.github.javaparser.ast.Generated; +import com.github.javaparser.ast.Node; import com.github.javaparser.ast.nodeTypes.NodeWithCondition; import com.github.javaparser.ast.observer.ObservableProperty; +import com.github.javaparser.ast.visitor.CloneVisitor; import com.github.javaparser.ast.visitor.GenericVisitor; import com.github.javaparser.ast.visitor.VoidVisitor; -import static com.github.javaparser.utils.Utils.assertNotNull; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.visitor.CloneVisitor; import com.github.javaparser.metamodel.ConditionalExprMetaModel; import com.github.javaparser.metamodel.JavaParserMetaModel; -import com.github.javaparser.TokenRange; -import java.util.function.Consumer; -import java.util.Optional; -import com.github.javaparser.ast.Generated; /** * The ternary conditional expression. @@ -201,4 +203,13 @@ public void ifConditionalExpr(Consumer action) { public Optional toConditionalExpr() { return Optional.of(this); } + + /* + * A reference conditional expression is a poly expression if it appears in an assignment context or an invocation context (§5.2. §5.3). + * Otherwise, it is a standalone expression. + */ + @Override + public boolean isPolyExpression() { + return appearsInAssignmentContext() || appearsInInvocationContext(); + } } diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/EnclosedExpr.java b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/EnclosedExpr.java index 414eeec05a..13f9cf31fb 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/EnclosedExpr.java +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/EnclosedExpr.java @@ -20,19 +20,21 @@ */ package com.github.javaparser.ast.expr; +import static com.github.javaparser.utils.Utils.assertNotNull; + +import java.util.Optional; +import java.util.function.Consumer; + +import com.github.javaparser.TokenRange; import com.github.javaparser.ast.AllFieldsConstructor; +import com.github.javaparser.ast.Generated; +import com.github.javaparser.ast.Node; import com.github.javaparser.ast.observer.ObservableProperty; +import com.github.javaparser.ast.visitor.CloneVisitor; import com.github.javaparser.ast.visitor.GenericVisitor; import com.github.javaparser.ast.visitor.VoidVisitor; -import java.util.Optional; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.visitor.CloneVisitor; import com.github.javaparser.metamodel.EnclosedExprMetaModel; import com.github.javaparser.metamodel.JavaParserMetaModel; -import com.github.javaparser.TokenRange; -import static com.github.javaparser.utils.Utils.assertNotNull; -import java.util.function.Consumer; -import com.github.javaparser.ast.Generated; /** * An expression between ( ). @@ -154,4 +156,13 @@ public void ifEnclosedExpr(Consumer action) { public Optional toEnclosedExpr() { return Optional.of(this); } + + /* + * On Parenthesized Expressions, if the contained expression is a poly expression (§15.2), the parenthesized expression is also a poly expression. Otherwise, it is a standalone expression. + * (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.8.5) + */ + @Override + public boolean isPolyExpression() { + return getInner().isPolyExpression(); + } } diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/Expression.java b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/Expression.java index 106c88f5ab..a3f66d40d0 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/Expression.java +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/Expression.java @@ -20,17 +20,21 @@ */ package com.github.javaparser.ast.expr; +import static com.github.javaparser.utils.CodeGenerationUtils.f; + +import java.util.Optional; +import java.util.function.Consumer; + +import com.github.javaparser.TokenRange; import com.github.javaparser.ast.AllFieldsConstructor; +import com.github.javaparser.ast.Generated; import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.nodeTypes.NodeWithOptionalScope; +import com.github.javaparser.ast.nodeTypes.NodeWithTypeArguments; import com.github.javaparser.ast.visitor.CloneVisitor; import com.github.javaparser.metamodel.ExpressionMetaModel; import com.github.javaparser.metamodel.JavaParserMetaModel; -import com.github.javaparser.TokenRange; import com.github.javaparser.resolution.types.ResolvedType; -import com.github.javaparser.ast.Generated; -import java.util.function.Consumer; -import static com.github.javaparser.utils.CodeGenerationUtils.f; -import java.util.Optional; /** * A base class for all expressions. @@ -790,4 +794,87 @@ public Optional toPatternExpr() { @Generated("com.github.javaparser.generator.core.node.TypeCastingGenerator") public void ifPatternExpr(Consumer action) { } + + /** + * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.2 + * @return true if the expression is a standalone expression + */ + public boolean isStandaloneExpression() { + return !isPolyExpression(); + } + + /** + * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.2 + * @return true if the expression is a poly expression + */ + public boolean isPolyExpression() { + return false; + } + + /* + * 6.5.6.2. Qualified Expression Names + * https://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.5.6.2 + */ + public boolean isQualified() { + return this instanceof NodeWithOptionalScope && ((NodeWithOptionalScope)this).getScope().isPresent(); + } + + /* + * Verify if the parent node is an assignment context. + */ + public final boolean appearsInAssignmentContext() { + if (getParentNode().isPresent() && getParentNode().get() instanceof Expression) { + return ((Expression)getParentNode().get()).isAssignmentContext(); + } + return false; + } + + /* + * Returns true if the expression is an assignment context. Default is false. + * https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.2 + * 5.2. Assignment Contexts: Assignment contexts allow the value of an expression to be assigned (§15.26) to a variable;... + */ + protected boolean isAssignmentContext() { + return false; + } + + /* + * Verify if the parent node is an invocation context. + */ + public final boolean appearsInInvocationContext() { + if (getParentNode().isPresent() && getParentNode().get() instanceof Expression) { + return ((Expression)getParentNode().get()).isInvocationContext(); + } + return false; + } + + /* + * Returns true if the expression is an invocation context. Default is false. + * https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.3 + * 5.3. Invocation Contexts + */ + protected boolean isInvocationContext() { + return false; + } + + /* + * returns true if the scope of this expression does not define an type argument or if the expression has not a scope (the expression is not qualified) + * or if there is a scope it uses <> to elide class type arguments + * For exemple : + * m() ==> true because there is no scope + * a.m() ==> true because the scope has no type arguments + * a<>.m() ==> true because the type argument is elided + * a.m() ==> false because the type argument is not elided + */ + public final boolean elidesTypeArguments() { + if (!(this instanceof NodeWithOptionalScope + && ((NodeWithOptionalScope) this).getScope().isPresent() + && this instanceof NodeWithTypeArguments)) { + return true; + } + Expression scope = (Expression) ((NodeWithOptionalScope) this).getScope().get(); + NodeWithTypeArguments nwta = (NodeWithTypeArguments)this; + return scope.elidesTypeArguments() + && (!nwta.getTypeArguments().isPresent() || nwta.isUsingDiamondOperator()); + } } diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/LambdaExpr.java b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/LambdaExpr.java index 8485c03744..e64970636e 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/LambdaExpr.java +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/LambdaExpr.java @@ -20,6 +20,11 @@ */ package com.github.javaparser.ast.expr; +import static com.github.javaparser.utils.Utils.assertNotNull; + +import java.util.Optional; +import java.util.function.Consumer; + import com.github.javaparser.TokenRange; import com.github.javaparser.ast.AllFieldsConstructor; import com.github.javaparser.ast.Generated; @@ -38,9 +43,6 @@ import com.github.javaparser.metamodel.DerivedProperty; import com.github.javaparser.metamodel.JavaParserMetaModel; import com.github.javaparser.metamodel.LambdaExprMetaModel; -import java.util.Optional; -import java.util.function.Consumer; -import static com.github.javaparser.utils.Utils.assertNotNull; /** *

A lambda expression

@@ -262,4 +264,12 @@ public void ifLambdaExpr(Consumer action) { public Optional toLambdaExpr() { return Optional.of(this); } + + /* + * Lambda expressions are always poly expressions + */ + @Override + public boolean isPolyExpression() { + return true; + } } diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/MethodCallExpr.java b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/MethodCallExpr.java index 2b07196e9f..de3673f7b2 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/MethodCallExpr.java +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/MethodCallExpr.java @@ -20,7 +20,15 @@ */ package com.github.javaparser.ast.expr; +import static com.github.javaparser.utils.Utils.assertNotNull; + +import java.util.Optional; +import java.util.function.Consumer; + +import com.github.javaparser.TokenRange; import com.github.javaparser.ast.AllFieldsConstructor; +import com.github.javaparser.ast.Generated; +import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.nodeTypes.NodeWithArguments; import com.github.javaparser.ast.nodeTypes.NodeWithOptionalScope; @@ -29,21 +37,16 @@ import com.github.javaparser.ast.observer.ObservableProperty; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.type.Type; +import com.github.javaparser.ast.visitor.CloneVisitor; import com.github.javaparser.ast.visitor.GenericVisitor; import com.github.javaparser.ast.visitor.VoidVisitor; -import java.util.Optional; -import static com.github.javaparser.utils.Utils.assertNotNull; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.visitor.CloneVisitor; -import com.github.javaparser.metamodel.MethodCallExprMetaModel; import com.github.javaparser.metamodel.JavaParserMetaModel; -import com.github.javaparser.TokenRange; +import com.github.javaparser.metamodel.MethodCallExprMetaModel; import com.github.javaparser.metamodel.OptionalProperty; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; -import java.util.function.Consumer; -import com.github.javaparser.ast.Generated; +import com.github.javaparser.resolution.types.ResolvedType; /** * A method call on an object or a class.
{@code circle.circumference()}
In a.<String>bb(15); a @@ -318,4 +321,68 @@ public ResolvedMethodDeclaration resolve() { public Optional toMethodCallExpr() { return Optional.of(this); } + + /* + * A method invocation expression is a poly expression if all of the following are true: + * 1. The invocation appears in an assignment context or an invocation context (§5.2, §5.3). + * 2. If the invocation is qualified (that is, any form of MethodInvocation except for the first), then + * the invocation elides TypeArguments to the left of the Identifier. + * 3. The method to be invoked, as determined by the following subsections, is generic (§8.4.4) and has a + * return type that mentions at least one of the method's type parameters. + * Otherwise, the method invocation expression is a standalone expression. + */ + @Override + public boolean isPolyExpression() { + // A method invocation expression is a poly expression if all of the following are true: + // + // 1. The invocation appears in an assignment context or an invocation context (§5.2, §5.3). + + if (!(appearsInAssignmentContext() || appearsInInvocationContext())) { + return false; + } + + // 2. If the invocation is qualified (that is, any form of MethodInvocation except for the form [MethodName ( + // [ArgumentList] )]), then the invocation elides TypeArguments to the left of the Identifier. + + if (isQualified() && !elidesTypeArguments()) { + return false; + } + + // 3. The method to be invoked, as determined by the following subsections, is generic (§8.4.4) and has a + // return type that mentions at least one of the method's type parameters. + // A method is generic if it declares one or more type variables (§4.4). + if (isGenericMethod() && hasParameterwithSameTypeThanResultType(resolve().getReturnType())) { + return true; // it's a poly expression + } + + // Otherwise, the method invocation expression is a standalone expression. + return false; + } + + /* + * A method is generic if it declares one or more type variables (§4.4). + * Not sure it's enough to verify that the type arguments list is empty or not. + */ + private boolean isGenericMethod() { + return getTypeArguments().isPresent() && !getTypeArguments().get().isEmpty(); + } + + /* + * return true if at least one of the method's type parameters has the same type as the specified type . + */ + private boolean hasParameterwithSameTypeThanResultType(ResolvedType resolvedReturnType) { + return getTypeArguments().isPresent() && getTypeArguments().get().stream().anyMatch(argType -> argType.resolve().isAssignableBy(resolvedReturnType)); + } + + + + /* + * Returns true if the expression is an invocation context. + * https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.3 + * 5.3. Invocation Contexts + */ + @Override + protected boolean isInvocationContext() { + return true; + } } diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/MethodReferenceExpr.java b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/MethodReferenceExpr.java index da3fdd944c..e51c77c4c8 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/MethodReferenceExpr.java +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/MethodReferenceExpr.java @@ -20,6 +20,12 @@ */ package com.github.javaparser.ast.expr; +import static com.github.javaparser.utils.Utils.assertNonEmpty; +import static com.github.javaparser.utils.Utils.assertNotNull; + +import java.util.Optional; +import java.util.function.Consumer; + import com.github.javaparser.TokenRange; import com.github.javaparser.ast.AllFieldsConstructor; import com.github.javaparser.ast.Generated; @@ -38,10 +44,6 @@ import com.github.javaparser.metamodel.OptionalProperty; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration; -import java.util.Optional; -import java.util.function.Consumer; -import static com.github.javaparser.utils.Utils.assertNonEmpty; -import static com.github.javaparser.utils.Utils.assertNotNull; /** * Method reference expressions introduced in Java 8 specifically designed to simplify lambda Expressions. @@ -234,4 +236,13 @@ public Optional toMethodReferenceExpr() { public ResolvedMethodDeclaration resolve() { return getSymbolResolver().resolveDeclaration(this, ResolvedMethodDeclaration.class); } + + /* + * Method reference expressions are always poly expressions + * (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html 15.13. Method Reference Expressions) + */ + @Override + public boolean isPolyExpression() { + return true; + } } diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/ObjectCreationExpr.java b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/ObjectCreationExpr.java index f84f22537f..0ffed99cfc 100644 --- a/javaparser-core/src/main/java/com/github/javaparser/ast/expr/ObjectCreationExpr.java +++ b/javaparser-core/src/main/java/com/github/javaparser/ast/expr/ObjectCreationExpr.java @@ -20,7 +20,15 @@ */ package com.github.javaparser.ast.expr; +import static com.github.javaparser.utils.Utils.assertNotNull; + +import java.util.Optional; +import java.util.function.Consumer; + +import com.github.javaparser.TokenRange; import com.github.javaparser.ast.AllFieldsConstructor; +import com.github.javaparser.ast.Generated; +import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.BodyDeclaration; import com.github.javaparser.ast.nodeTypes.NodeWithArguments; @@ -31,21 +39,15 @@ import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.type.ClassOrInterfaceType; import com.github.javaparser.ast.type.Type; +import com.github.javaparser.ast.visitor.CloneVisitor; import com.github.javaparser.ast.visitor.GenericVisitor; import com.github.javaparser.ast.visitor.VoidVisitor; -import java.util.Optional; -import static com.github.javaparser.utils.Utils.assertNotNull; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.visitor.CloneVisitor; -import com.github.javaparser.metamodel.ObjectCreationExprMetaModel; import com.github.javaparser.metamodel.JavaParserMetaModel; -import com.github.javaparser.TokenRange; +import com.github.javaparser.metamodel.ObjectCreationExprMetaModel; import com.github.javaparser.metamodel.OptionalProperty; import com.github.javaparser.resolution.Resolvable; import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration; -import java.util.function.Consumer; -import com.github.javaparser.ast.Generated; /** * A constructor call. @@ -369,4 +371,14 @@ public ResolvedConstructorDeclaration resolve() { public Optional toObjectCreationExpr() { return Optional.of(this); } + + /* + * A class instance creation expression is a poly expression (§15.2) if it uses the diamond form for type + * arguments to the class, and it appears in an assignment context or an invocation context (§5.2, §5.3). + * Otherwise, it is a standalone expression. + */ + @Override + public boolean isPolyExpression() { + return isUsingDiamondOperator() && (appearsInInvocationContext() || appearsInAssignmentContext()); + } } diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java index 1226c675a7..f22072a8f4 100644 --- a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java @@ -328,8 +328,19 @@ else if (thenExpr.isReference() && elseExpr.isPrimitive() /* * Otherwise, the conditional expression is a reference conditional expression. - * A reference conditional expression is a poly expression if it appears in an assignment context or an invocation context (§5.2. §5.3). + * A reference conditional expression is a poly expression if it appears in an assignment context or an + * invocation context (§5.2. §5.3). * Otherwise, it is a standalone expression. + * The type of a poly reference conditional expression is the same as its target type. + * The type of a standalone reference conditional expression is determined as follows: + * If the second and third operands have the same type (which may be the null type), then that is the type of + * the conditional expression. + * If the type of one of the second and third operands is the null type, and the type of the other operand is a + * reference type, then the type of the conditional expression is that reference type. + * Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that + * results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing + * conversion to S2. The type of the conditional expression is the result of applying capture conversion + * (§5.1.10) to lub(T1, T2). * TODO : must be implemented */ return node.getThenExpr().accept(this, solveLambdas); diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelper.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelper.java index 00dd442d1a..e5801340a0 100644 --- a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelper.java +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelper.java @@ -23,17 +23,9 @@ import java.util.List; -import com.github.javaparser.ast.Node; -import com.github.javaparser.ast.expr.ConditionalExpr; -import com.github.javaparser.ast.expr.EnclosedExpr; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.LambdaExpr; -import com.github.javaparser.ast.expr.MethodCallExpr; -import com.github.javaparser.ast.expr.MethodReferenceExpr; -import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.stmt.BlockStmt; -import com.github.javaparser.ast.stmt.ExpressionStmt; -import com.github.javaparser.ast.stmt.ReturnStmt; import com.github.javaparser.ast.type.UnknownType; import com.github.javaparser.resolution.types.ResolvedType; import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; @@ -44,116 +36,6 @@ */ public class ExpressionHelper { - /** - * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.2 - * @return - */ - public static boolean isStandaloneExpression(Expression expression) { - return !isPolyExpression(expression); - } - - /** - * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.2 - * @return - */ - public static boolean isPolyExpression(Expression expression) { - // On Parenthesized Expressions, if the contained expression is a poly expression (§15.2), the parenthesized expression is also a poly expression. Otherwise, it is a standalone expression. - // (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.8.5) - if (expression instanceof EnclosedExpr) { - return isPolyExpression(((EnclosedExpr) expression).getInner()); - } - if (expression instanceof ObjectCreationExpr) { - // A class instance creation expression is a poly expression (§15.2) if it uses the diamond form for type - // arguments to the class, and it appears in an assignment context or an invocation context (§5.2, §5.3). - // Otherwise, it is a standalone expression. - ObjectCreationExpr objectCreationExpr = (ObjectCreationExpr)expression; - if (objectCreationExpr.isUsingDiamondOperator()) { - throw new UnsupportedOperationException(expression.toString()); - } else { - return false; - } - } - if (expression instanceof MethodCallExpr) { - MethodCallExpr methodCallExpr = (MethodCallExpr)expression; - - // A method invocation expression is a poly expression if all of the following are true: - // - // 1. The invocation appears in an assignment context or an invocation context (§5.2, §5.3). - - if (!appearsInAssignmentContext(expression) || appearsInInvocationContext(expression)) { - return false; - } - - // 2. If the invocation is qualified (that is, any form of MethodInvocation except for the first), then - // the invocation elides TypeArguments to the left of the Identifier. - - if (isQualified(methodCallExpr) && !elidesTypeArguments(methodCallExpr)) { - return false; - } - - // 3. The method to be invoked, as determined by the following subsections, is generic (§8.4.4) and has a - // return type that mentions at least one of the method's type parameters. - - //boolean condition3 =; - throw new UnsupportedOperationException(expression.toString()); - - // Otherwise, the method invocation expression is a standalone expression. - //return true; - } - // Method reference expressions are always poly expressions (https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html 15.13. Method Reference Expressions) - if (expression instanceof MethodReferenceExpr) { - return true; - } - if (expression instanceof ConditionalExpr) { - throw new UnsupportedOperationException(expression.toString()); - } - // Lambda expressions are always poly expressions - if (expression instanceof LambdaExpr) { - return true; - } - return false; - } - - private static boolean elidesTypeArguments(MethodCallExpr methodCall) { - throw new UnsupportedOperationException(); - } - - private static boolean isQualified(MethodCallExpr methodCall) { - throw new UnsupportedOperationException(); - } - - // Not sure if should look if the parent is an assignment context - private static boolean appearsInAssignmentContext(Expression expression) { - if (expression.getParentNode().isPresent()) { - Node parent = expression.getParentNode().get(); - if (parent instanceof ExpressionStmt) { - return false; - } - if (parent instanceof MethodCallExpr) { - return false; - } - if (parent instanceof ReturnStmt) { - return false; - } - throw new UnsupportedOperationException(parent.getClass().getCanonicalName()); - } - return false; - } - - private static boolean appearsInInvocationContext(Expression expression) { - if (expression.getParentNode().isPresent()) { - Node parent = expression.getParentNode().get(); - if (parent instanceof ExpressionStmt) { - return false; - } - if (parent instanceof MethodCallExpr) { - return true; - } - throw new UnsupportedOperationException(parent.getClass().getCanonicalName()); - } - return false; - } - public static boolean isExplicitlyTyped(LambdaExpr lambdaExpr) { return lambdaExpr.getParameters().stream().allMatch(p -> !(p.getType() instanceof UnknownType)); } diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInference.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInference.java index da3d5bf8e4..d07d21934a 100644 --- a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInference.java +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInference.java @@ -21,7 +21,16 @@ package com.github.javaparser.symbolsolver.resolution.typeinference; -import com.github.javaparser.ast.expr.*; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +import com.github.javaparser.ast.expr.ConditionalExpr; +import com.github.javaparser.ast.expr.EnclosedExpr; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.LambdaExpr; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.MethodReferenceExpr; import com.github.javaparser.ast.type.UnknownType; import com.github.javaparser.resolution.MethodUsage; import com.github.javaparser.resolution.declarations.ResolvedInterfaceDeclaration; @@ -35,12 +44,6 @@ import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.ThrowsBound; import com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas.ExpressionCompatibleWithType; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; - -import static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isStandaloneExpression; - /** * The API exposed by the TypeInference subsystem. * @@ -675,11 +678,11 @@ private Optional testForApplicabilityByStrictInvocation(Li Expression ei = es.get(i); ResolvedType fi = Fs.get(i); if (isPertinentToApplicability(ei)) { - if (isStandaloneExpression(ei) && JavaParserFacade.get(typeSolver).getType(ei).isPrimitive() + if (ei.isStandaloneExpression() && JavaParserFacade.get(typeSolver).getType(ei).isPrimitive() && fi.isReferenceType()) { return Optional.empty(); } - if (fi.isPrimitive() && (!isStandaloneExpression(ei) || !JavaParserFacade.get(typeSolver).getType(ei).isPrimitive())) { + if (fi.isPrimitive() && (!ei.isStandaloneExpression() || !JavaParserFacade.get(typeSolver).getType(ei).isPrimitive())) { return Optional.empty(); } } diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java index ad7fa850b7..1a2f696381 100644 --- a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java @@ -21,28 +21,43 @@ package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas; -import com.github.javaparser.ast.expr.*; -import com.github.javaparser.ast.stmt.*; +import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isCompatibleInALooseInvocationContext; +import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType; +import static java.util.stream.Collectors.toList; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.github.javaparser.ast.expr.ConditionalExpr; +import com.github.javaparser.ast.expr.EnclosedExpr; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.LambdaExpr; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.MethodReferenceExpr; +import com.github.javaparser.ast.expr.ObjectCreationExpr; +import com.github.javaparser.ast.stmt.BlockStmt; +import com.github.javaparser.ast.stmt.ExpressionStmt; +import com.github.javaparser.ast.stmt.ReturnStmt; +import com.github.javaparser.ast.stmt.Statement; import com.github.javaparser.ast.type.UnknownType; import com.github.javaparser.resolution.types.ResolvedType; import com.github.javaparser.resolution.types.ResolvedTypeVariable; import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic; import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; -import com.github.javaparser.symbolsolver.resolution.typeinference.*; +import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet; +import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula; +import com.github.javaparser.symbolsolver.resolution.typeinference.ControlFlowLogic; +import com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper; +import com.github.javaparser.symbolsolver.resolution.typeinference.InferenceVariable; +import com.github.javaparser.symbolsolver.resolution.typeinference.MethodType; +import com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper; +import com.github.javaparser.symbolsolver.resolution.typeinference.TypeInference; +import com.github.javaparser.symbolsolver.resolution.typeinference.TypeInferenceCache; import com.github.javaparser.utils.Pair; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isPolyExpression; -import static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isStandaloneExpression; -import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isCompatibleInALooseInvocationContext; -import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType; -import static java.util.stream.Collectors.*; - /** * An expression is compatible in a loose invocation context with type T * @@ -75,14 +90,14 @@ public ReductionResult reduce(BoundSet currentBoundSet) { // Otherwise, if the expression is a standalone expression (§15.2) of type S, the constraint reduces // to ‹S → T›. - if (isStandaloneExpression(expression)) { + if (expression.isStandaloneExpression()) { ResolvedType s = JavaParserFacade.get(typeSolver).getType(expression, false); return ReductionResult.empty().withConstraint(new TypeCompatibleWithType(typeSolver, s, T)); } // Otherwise, the expression is a poly expression (§15.2). The result depends on the form of the expression: - if (isPolyExpression(expression)) { + if (expression.isPolyExpression()) { // - If the expression is a parenthesized expression of the form ( Expression' ), the constraint reduces // to ‹Expression' → T›. diff --git a/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/resolution/PolyExpressionResolutionTest.java b/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/resolution/PolyExpressionResolutionTest.java new file mode 100755 index 0000000000..c87c410334 --- /dev/null +++ b/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/resolution/PolyExpressionResolutionTest.java @@ -0,0 +1,122 @@ +package com.github.javaparser.symbolsolver.resolution; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.MethodCallExpr; + +public class PolyExpressionResolutionTest extends AbstractResolutionTest { + + @BeforeEach + void setup() { + } + + @Test + void methodReferenceExpressionAsPolyExpression() { + Expression expr = StaticJavaParser.parseExpression("String::length"); + assertTrue(expr.isPolyExpression()); + assertFalse(expr.isStandaloneExpression()); + } + + @Test + void lambdaExpressionAsPolyExpression() { + Expression expr = StaticJavaParser.parseExpression("(s) -> s.toString()"); + assertTrue(expr.isPolyExpression()); + assertFalse(expr.isStandaloneExpression()); + } + + @Test + void parenthesizedExpressionAsStandaloneExpression() { + Expression expr = StaticJavaParser.parseExpression("(-1)"); + assertTrue(expr.isStandaloneExpression()); + assertFalse(expr.isPolyExpression()); + } + + @Test + void objectCreationPolyExpressionTest() { + Expression expr = StaticJavaParser.parseExpression("new ArrayList<>()"); + // see issue https://github.com/javaparser/javaparser/issues/2985 + assertFalse(expr.isPolyExpression()); + assertTrue(expr.isStandaloneExpression()); + } + + @Test + void objectCreationStandaloneExpressionTest() { + Expression expr = StaticJavaParser.parseExpression("new ArrayList()"); + assertFalse(expr.isPolyExpression()); + assertTrue(expr.isStandaloneExpression()); + + expr = StaticJavaParser.parseExpression("new ArrayList<>().clear()"); + assertFalse(expr.isPolyExpression()); + assertTrue(expr.isStandaloneExpression()); + + expr = StaticJavaParser.parseExpression("new ArrayList()"); + assertFalse(expr.isPolyExpression()); + assertTrue(expr.isStandaloneExpression()); + } + + @Test + void methodCallExpressionStandaloneExpressionInMethodCallContextTest() { + Expression expr = StaticJavaParser.parseExpression("m(s.toString())").findAll(MethodCallExpr.class).get(1); + assertFalse(expr.isPolyExpression()); + assertTrue(expr.isStandaloneExpression()); + } + + @Test + void methodCallExpressionStandaloneExpressionInAssignementContextTest() { + Expression expr = StaticJavaParser.parseExpression("x = s.toString()").findAll(MethodCallExpr.class).get(0); + assertFalse(expr.isPolyExpression()); + assertTrue(expr.isStandaloneExpression()); + } + + @Test + void methodCallExpressionPolyExpressionInAssignementContextTest() { + Expression expr = StaticJavaParser.parseExpression("same = Util.compare(p1, p2)").findAll(MethodCallExpr.class).get(0); + assertFalse(expr.isPolyExpression()); + assertTrue(expr.isStandaloneExpression()); + } + + @Test + void elidesTypeArgumentsTest() { + Expression expr = StaticJavaParser.parseExpression("m()"); + assertTrue(expr.elidesTypeArguments()); + expr = StaticJavaParser.parseExpression("a.m()").findFirst(MethodCallExpr.class).get(); + assertTrue(expr.elidesTypeArguments()); + expr = StaticJavaParser.parseExpression("new A().m()").findFirst(MethodCallExpr.class).get(); + assertTrue(expr.elidesTypeArguments()); + expr = StaticJavaParser.parseExpression("new A().<>m()").findFirst(MethodCallExpr.class).get(); + assertTrue(expr.elidesTypeArguments()); + expr = StaticJavaParser.parseExpression("new A().m()").findFirst(MethodCallExpr.class).get(); + assertFalse(expr.elidesTypeArguments()); + } + + @Test + void appearsInAssignmentContextTest() { + Expression expr = StaticJavaParser.parseExpression("a = m()").findFirst(MethodCallExpr.class).get(); + assertTrue(expr.appearsInAssignmentContext()); + } + + @Test + void notAppearsInAssignmentContextTest() { + Expression expr = StaticJavaParser.parseExpression("a.m()").findFirst(MethodCallExpr.class).get(); + assertFalse(expr.appearsInAssignmentContext()); + } + + @Test + void notAppearsInInvocationContextTest() { + Expression expr = StaticJavaParser.parseExpression("a = m()").findFirst(MethodCallExpr.class).get(); + assertFalse(expr.appearsInInvocationContext()); + } + + @Test + void appearsInInvocationContextTest() { + Expression expr = StaticJavaParser.parseExpression("a().m()").findAll(MethodCallExpr.class).get(1); + assertTrue(expr.appearsInInvocationContext()); + } + +} diff --git a/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/resolution/ReferenceTypeResolutionTest.java b/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/resolution/ReferenceTypeResolutionTest.java index 491fa2138a..d30cb1e4ca 100755 --- a/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/resolution/ReferenceTypeResolutionTest.java +++ b/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/resolution/ReferenceTypeResolutionTest.java @@ -40,7 +40,7 @@ void objectTest() { } @Test - void cannotUnboxTypeTest() { + void cannotUnboxReferenceTypeTest() { String code = "class A { Object o; }"; ResolvedReferenceType rt = StaticJavaParser.parse(code).findFirst(FieldDeclaration.class).get().resolve().getType().asReferenceType(); assertFalse(rt.isUnboxable()); diff --git a/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelperTest.java b/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelperTest.java deleted file mode 100755 index f02eb09a11..0000000000 --- a/javaparser-symbol-solver-testing/src/test/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelperTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2017-2019 The JavaParser Team. - * - * This file is part of JavaParser. - * - * JavaParser can be used either under the terms of - * a) the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * b) the terms of the Apache License - * - * You should have received a copy of both licenses in LICENCE.LGPL and - * LICENCE.APACHE. Please refer to those files for details. - * - * JavaParser is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - */ - -package com.github.javaparser.symbolsolver.resolution.typeinference; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -import com.github.javaparser.StaticJavaParser; -import com.github.javaparser.ast.expr.Expression; -import com.github.javaparser.symbolsolver.resolution.AbstractResolutionTest; - -class ExpressionHelperTest extends AbstractResolutionTest { - - @Test - void parenthesizedExpressionAsStandaloneExpression() { - Expression expression = StaticJavaParser.parseExpression("(-1)"); - assertTrue(ExpressionHelper.isStandaloneExpression(expression)); - } - - @Test - void methodReferenceExpressionAsPolyExpression() { - Expression expression = StaticJavaParser.parseExpression("String::length"); - assertTrue(ExpressionHelper.isPolyExpression(expression)); - } - - @Test - void lambdaExpressionAsPolyExpression() { - Expression expression = StaticJavaParser.parseExpression("(s) -> s.toString()"); - assertTrue(ExpressionHelper.isPolyExpression(expression)); - } - -}