This is an automated email from the ASF dual-hosted git repository. joshtynjala pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/royale-compiler.git
commit d8d3b42351f138319758b4c84d67bb100155e7fa Author: Josh Tynjala <[email protected]> AuthorDate: Wed May 24 14:51:40 2023 -0700 New compiler option --infer-types to enable type inference for variables, constants, parameters, and return types If no type is specified, but the variable is assigned in the declaration, the type is inferred automatically from the right hand side of the assignment. For functions, the return type is inferred from the types of return statements (or lack thereof), resolving a common base type, if necessary. These types are used not only for compile time problem checking, but also in the generated ABC bytecode and the generated JS, the same as if the type was explicitly declared. This ensures maximum performance (and best optimization by Closure Compiler), and maximum productivity in editors and IDEs that use types for code intelligence. Disabled by default and must opt-in to use (we could consider making it opt-out in the future, though). --- .../royale/compiler/config/Configuration.java | 18 + .../royale/compiler/projects/ICompilerProject.java | 5 + .../codegen/js/goog/IJSGoogDocEmitter.java | 4 +- .../internal/codegen/js/goog/JSGoogDocEmitter.java | 69 +- .../codegen/js/royale/JSRoyaleDocEmitter.java | 21 +- .../codegen/js/royale/TestRoyaleInferTypes.java | 584 ++++++ .../royale/compiler/internal/test/ASTestBase.java | 7 + .../internal/as/codegen/ABCGeneratingReducer.java | 22 + .../internal/definitions/AccessorDefinition.java | 9 +- .../internal/definitions/FunctionDefinition.java | 17 + .../internal/definitions/GetterDefinition.java | 26 + .../internal/definitions/ParameterDefinition.java | 21 + .../internal/definitions/SetterDefinition.java | 23 + .../internal/definitions/VariableDefinition.java | 21 + .../internal/parsing/as/ConfigProcessor.java | 6 + .../compiler/internal/projects/ASCProject.java | 6 + .../compiler/internal/projects/RoyaleProject.java | 15 + .../projects/RoyaleProjectConfigurator.java | 2 + .../semantics/MethodBodySemanticChecker.java | 8 + .../compiler/internal/semantics/SemanticUtils.java | 420 ++++- .../compiler/internal/tree/as/ReturnNode.java | 15 +- compiler/src/test/java/as/ASFeatureTestsBase.java | 7 + compiler/src/test/java/as/ASInferTypesTests.java | 1912 ++++++++++++++++++++ 23 files changed, 3211 insertions(+), 27 deletions(-) diff --git a/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java b/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java index 64b7de260..78e15f8d1 100644 --- a/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java +++ b/compiler-common/src/main/java/org/apache/royale/compiler/config/Configuration.java @@ -6413,4 +6413,22 @@ public class Configuration watch = b; } + // + // 'compiler.infer-types' + // + + private boolean inferTypes = false; + + public boolean getInferTypes() + { + return inferTypes; + } + + @Config + @Mapping({ "compiler", "infer-types" }) + public void setInferTypes(ConfigurationValue cv, boolean b) + { + inferTypes = b; + } + } diff --git a/compiler-common/src/main/java/org/apache/royale/compiler/projects/ICompilerProject.java b/compiler-common/src/main/java/org/apache/royale/compiler/projects/ICompilerProject.java index 0cbd0b53b..ca69ae5e7 100644 --- a/compiler-common/src/main/java/org/apache/royale/compiler/projects/ICompilerProject.java +++ b/compiler-common/src/main/java/org/apache/royale/compiler/projects/ICompilerProject.java @@ -286,5 +286,10 @@ public interface ICompilerProject * @return True if strict identifier naming is enforced. */ boolean getStrictIdentifierNames(); + + /** + * @return True if type inference is enabled. + */ + boolean getInferTypes(); } diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/codegen/js/goog/IJSGoogDocEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/codegen/js/goog/IJSGoogDocEmitter.java index 33b2afa08..167a7c8eb 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/codegen/js/goog/IJSGoogDocEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/codegen/js/goog/IJSGoogDocEmitter.java @@ -122,7 +122,7 @@ public interface IJSGoogDocEmitter extends IJSDocEmitter void emitOverride(IFunctionNode node); - void emitParam(IParameterNode node, String packageName); + void emitParam(IParameterNode node, String packageName, ICompilerProject project); void emitPublic(IASNode node); @@ -132,7 +132,7 @@ public interface IJSGoogDocEmitter extends IJSDocEmitter void emitInternal(IASNode node); - void emitReturn(IFunctionNode node, String packageName); + void emitReturn(IFunctionNode node, String packageName, ICompilerProject project); void emitThis(ITypeDefinition node, String packageName); diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/goog/JSGoogDocEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/goog/JSGoogDocEmitter.java index f5620b53e..75a90c623 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/goog/JSGoogDocEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/goog/JSGoogDocEmitter.java @@ -200,13 +200,25 @@ public class JSGoogDocEmitter extends JSDocEmitter implements IJSGoogDocEmitter } IExpressionNode enode = pnode.getNameExpressionNode(); - emitParam(pnode, enode.resolveType(project).getPackageName()); + emitParam(pnode, enode.resolveType(project).getPackageName(), project); } if (!node.isConstructor()) { // @return String returnType = node.getReturnType(); + if (project.getInferTypes() && (returnType == null || returnType.isEmpty())) + { + ITypeDefinition resolvedTypeDef = SemanticUtils.resolveFunctionInferredReturnType(node, project); + if (resolvedTypeDef != null) + { + returnType = resolvedTypeDef.getQualifiedName(); + } + else + { + returnType = "*"; + } + } if (returnType != "" && returnType != ASEmitterTokens.VOID.getToken()) { @@ -217,7 +229,7 @@ public class JSGoogDocEmitter extends JSDocEmitter implements IJSGoogDocEmitter hasDoc = true; } - emitReturn(node, node.getPackageName()); + emitReturn(node, node.getPackageName(), project); } // @override @@ -302,7 +314,7 @@ public class JSGoogDocEmitter extends JSDocEmitter implements IJSGoogDocEmitter } @Override - public void emitParam(IParameterNode node, String packageName) + public void emitParam(IParameterNode node, String packageName, ICompilerProject project) { String postfix = (node.getDefaultValue() == null) ? "" : ASEmitterTokens.EQUAL.getToken(); @@ -315,6 +327,14 @@ public class JSGoogDocEmitter extends JSDocEmitter implements IJSGoogDocEmitter else { String typeName = node.getVariableType(); + if (project.getInferTypes() && typeName.isEmpty()) + { + ITypeDefinition resolvedTypeDef = SemanticUtils.resolveVariableInferredType(node, project); + if (resolvedTypeDef != null) + { + typeName = resolvedTypeDef.getQualifiedName(); + } + } if (packageName.length() > 0 && typeName.indexOf(packageName) > -1) { String[] parts = typeName.split("\\."); @@ -355,10 +375,22 @@ public class JSGoogDocEmitter extends JSDocEmitter implements IJSGoogDocEmitter } @Override - public void emitReturn(IFunctionNode node, String packageName) + public void emitReturn(IFunctionNode node, String packageName, ICompilerProject project) { String rtype = node.getReturnType(); - if (rtype != null) + if (project.getInferTypes() && (rtype == null || rtype.isEmpty())) + { + ITypeDefinition resolvedTypeDef = SemanticUtils.resolveFunctionInferredReturnType(node, project); + if (resolvedTypeDef != null) + { + rtype = resolvedTypeDef.getQualifiedName(); + } + else + { + rtype = "*"; + } + } + if (rtype != null && rtype != ASEmitterTokens.VOID.getToken()) { emitJSDocLine(ASEmitterTokens.RETURN, convertASTypeToJS(rtype, packageName)); @@ -374,9 +406,18 @@ public class JSGoogDocEmitter extends JSDocEmitter implements IJSGoogDocEmitter @Override public void emitType(IASNode node, String packageName, ICompilerProject project) { - String type = ((IVariableNode) node).getVariableType(); - if (((IVariableNode) node).getVariableTypeNode() instanceof TypedExpressionNode) { - ITypeDefinition elemenTypeDef = ((TypedExpressionNode)(((IVariableNode) node).getVariableTypeNode())).getTypeNode().resolveType(project); + IVariableNode varNode = (IVariableNode) node; + String type = varNode.getVariableType(); + if (project.getInferTypes() && type.isEmpty()) + { + ITypeDefinition resolvedTypeDef = SemanticUtils.resolveVariableInferredType(varNode, project); + if (resolvedTypeDef != null) + { + type = resolvedTypeDef.getQualifiedName(); + } + } + if (varNode.getVariableTypeNode() instanceof TypedExpressionNode) { + ITypeDefinition elemenTypeDef = ((TypedExpressionNode)(varNode.getVariableTypeNode())).getTypeNode().resolveType(project); if (elemenTypeDef != null) { type = "Vector.<" + convertASTypeToJS(elemenTypeDef.getQualifiedName(),"") @@ -395,9 +436,17 @@ public class JSGoogDocEmitter extends JSDocEmitter implements IJSGoogDocEmitter convertASTypeToJS(type, packageName)); } - public void emitTypeShort(IASNode node, String packageName, ICompilerProject project) + public void emitTypeShort(IVariableNode node, String packageName, ICompilerProject project) { - String type = ((IVariableNode) node).getVariableType(); + String type = node.getVariableType(); + if (project.getInferTypes() && type.isEmpty()) + { + ITypeDefinition resolvedTypeDef = SemanticUtils.resolveVariableInferredType(node, project); + if (resolvedTypeDef != null) + { + type = resolvedTypeDef.getQualifiedName(); + } + } if (((IVariableNode) node).getVariableTypeNode() instanceof TypedExpressionNode) { ITypeDefinition elemenTypeDef = ((TypedExpressionNode)(((IVariableNode) node).getVariableTypeNode())).getTypeNode().resolveType(project); if (elemenTypeDef != null) { diff --git a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleDocEmitter.java b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleDocEmitter.java index d805eecb2..25861cb9f 100644 --- a/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleDocEmitter.java +++ b/compiler-jx/src/main/java/org/apache/royale/compiler/internal/codegen/js/royale/JSRoyaleDocEmitter.java @@ -41,6 +41,7 @@ import org.apache.royale.compiler.internal.codegen.js.goog.JSGoogDocEmitterToken import org.apache.royale.compiler.internal.codegen.js.jx.BindableEmitter; import org.apache.royale.compiler.internal.projects.RoyaleJSProject; import org.apache.royale.compiler.internal.scopes.ASScope; +import org.apache.royale.compiler.internal.semantics.SemanticUtils; import org.apache.royale.compiler.problems.PublicVarWarningProblem; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.tree.ASTNodeID; @@ -304,7 +305,7 @@ public class JSRoyaleDocEmitter extends JSGoogDocEmitter if (tdef == null) continue; - emitParam(pnode, project.getActualPackageName(tdef.getPackageName())); + emitParam(pnode, project.getActualPackageName(tdef.getPackageName()), project); } } @@ -314,6 +315,18 @@ public class JSRoyaleDocEmitter extends JSGoogDocEmitter { // @return String returnType = node.getReturnType(); + if (project.getInferTypes() && (returnType == null || returnType.isEmpty())) + { + ITypeDefinition resolvedTypeDef = SemanticUtils.resolveFunctionInferredReturnType(node, project); + if (resolvedTypeDef != null) + { + returnType = resolvedTypeDef.getQualifiedName(); + } + else + { + returnType = "*"; + } + } if (returnType != "" && returnType != ASEmitterTokens.VOID.getToken()) { @@ -334,7 +347,7 @@ public class JSRoyaleDocEmitter extends JSGoogDocEmitter String packageName = ""; packageName = tdef != null ? tdef.getPackageName() : ""; - emitReturn(node, project.getActualPackageName(packageName)); + emitReturn(node, project.getActualPackageName(packageName), project); } } @@ -500,7 +513,7 @@ public class JSRoyaleDocEmitter extends JSGoogDocEmitter ITypeDefinition tdef = ((IFunctionDefinition) node.getDefinition()) .resolveReturnType(project); - emitReturn((IFunctionNode) node, tdef.getPackageName()); + emitReturn((IFunctionNode) node, tdef.getPackageName(), project); } IParameterNode[] parameters = ((IFunctionNode) node) @@ -517,7 +530,7 @@ public class JSRoyaleDocEmitter extends JSGoogDocEmitter } IExpressionNode enode = pnode.getNameExpressionNode(); - emitParam(pnode, enode.resolveType(project).getPackageName()); + emitParam(pnode, enode.resolveType(project).getPackageName(), project); } if (hasDoc) diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleInferTypes.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleInferTypes.java new file mode 100644 index 000000000..c30ba199a --- /dev/null +++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/codegen/js/royale/TestRoyaleInferTypes.java @@ -0,0 +1,584 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.royale.compiler.internal.codegen.js.royale; + +import org.apache.royale.compiler.driver.IBackend; +import org.apache.royale.compiler.internal.driver.js.goog.JSGoogConfiguration; +import org.apache.royale.compiler.internal.driver.js.royale.RoyaleBackend; +import org.apache.royale.compiler.internal.projects.RoyaleJSProject; +import org.apache.royale.compiler.internal.test.ASTestBase; +import org.apache.royale.compiler.tree.as.IASNode; +import org.apache.royale.compiler.tree.as.IClassNode; +import org.apache.royale.compiler.tree.as.IFileNode; +import org.apache.royale.compiler.tree.as.IFunctionNode; +import org.apache.royale.compiler.tree.as.IGetterNode; +import org.apache.royale.compiler.tree.as.ISetterNode; +import org.apache.royale.compiler.tree.as.IVariableNode; +import org.junit.Test; + +public class TestRoyaleInferTypes extends ASTestBase +{ + @Override + public void setUp() + { + backend = createBackend(); + project = new RoyaleJSProject(workspace, backend); + project.setInferTypes(true); + project.config = new JSGoogConfiguration(); + super.setUp(); + } + + // ----- local variables + + @Test + public void testInferLocalVariableWithStringDefaultValue() + { + IVariableNode node = getVariable("var s = \"hello\""); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {string} */ s = \"hello\""); + } + + @Test + public void testInferLocalVariableWithBooleanDefaultValue() + { + IVariableNode node = getVariable("var b = true"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {boolean} */ b = true"); + } + + @Test + public void testInferLocalVariableWithNumberDefaultValue() + { + IVariableNode node = getVariable("var n = 123.4"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {number} */ n = 123.4"); + } + + @Test + public void testInferLocalVariableWithArrayDefaultValue() + { + IVariableNode node = getVariable("var a = []"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {Array} */ a = []"); + } + + @Test + public void testInferLocalVariableWithObjectDefaultValue() + { + IVariableNode node = getVariable("var o = {}"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {Object} */ o = {}"); + } + + @Test + public void testInferLocalVariableWithDateDefaultValue() + { + IVariableNode node = getVariable("var d = new Date();"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {Date} */ d = new Date()"); + } + + @Test + public void testInferLocalVariableWithNullDefaultValue() + { + IVariableNode node = getVariable("var x = null;"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {*} */ x = null"); + } + + @Test + public void testInferLocalVariableWithUndefinedDefaultValue() + { + IVariableNode node = getVariable("var x = undefined;"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {*} */ x = undefined"); + } + + @Test + public void testInferLocalVariableWithNoDefaultValue() + { + IVariableNode node = getVariable("var x;"); + asBlockWalker.visitVariable(node); + assertOut("var /** @type {*} */ x"); + } + + // ----- parameters + + @Test + public void testInferParameterWithStringDefaultValue() + { + IFunctionNode node = getMethod("function f(s = \"hello\"):void {}"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @param {string=} s\n */\nRoyaleTest_A.prototype.f = function(s) {\n s = typeof s !== 'undefined' ? s : \"hello\";\n}"); + } + + @Test + public void testInferParameterWithNullDefaultValue() + { + IFunctionNode node = getMethod("function f(x = null):void {}"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @param {*=} x\n */\nRoyaleTest_A.prototype.f = function(x) {\n x = typeof x !== 'undefined' ? x : null;\n}"); + } + + @Test + public void testInferParameterWithUndefinedDefaultValue() + { + IFunctionNode node = getMethod("function f(x = undefined):void {}"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @param {*=} x\n */\nRoyaleTest_A.prototype.f = function(x) {\n x = typeof x !== 'undefined' ? x : undefined;\n}"); + } + + @Test + public void testInferParameterWithNoDefaultValue() + { + IFunctionNode node = getMethod("function f(x):void {}"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @param {*} x\n */\nRoyaleTest_A.prototype.f = function(x) {\n}"); + } + + // ----- returns + + @Test + public void testInferReturnWithStringDefaultValue() + { + IFunctionNode node = getMethod("function f() { return \"hello\" }"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @return {string}\n */\nRoyaleTest_A.prototype.f = function() {\n return \"hello\";\n}"); + } + + @Test + public void testInferReturnWithNoReturnStatements() + { + IFunctionNode node = getMethod("function f() {}"); + asBlockWalker.visitFunction(node); + // inferred as void, so no comment is required + assertOut("RoyaleTest_A.prototype.f = function() {\n}"); + } + + @Test + public void testInferReturnWithEmptyReturnStatement() + { + IFunctionNode node = getMethod("function f() { return; }"); + asBlockWalker.visitFunction(node); + // inferred as void, so no comment is required + assertOut("RoyaleTest_A.prototype.f = function() {\n return;\n}"); + } + + @Test + public void testInferReturnWithNullDefaultValue() + { + IFunctionNode node = getMethod("function f() { return null; }"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @return {*}\n */\nRoyaleTest_A.prototype.f = function() {\n return null;\n}"); + } + + @Test + public void testInferReturnWithUndefinedDefaultValue() + { + IFunctionNode node = getMethod("function f() { return undefined; }"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @return {*}\n */\nRoyaleTest_A.prototype.f = function() {\n return undefined;\n}"); + } + + @Test + public void testInferReturnWithOverrideAndSuperInferredReturn() + { + IFileNode node = compileAS( + "package { public class A {} }\n" + + "class C { public function s() { return \"hello\"; } }\n" + + "class D extends C { override public function s() { return null; } }"); + IClassNode d = null; + for(int i = 0; i < node.getChildCount(); i++) + { + IASNode child = node.getChild(i); + if (child instanceof IClassNode) + { + IClassNode childClass = (IClassNode) child; + if ("D".equals(childClass.getName())) + { + d = childClass; + break; + } + } + } + asBlockWalker.visitClass(d); + // overrides don't need to specify type + assertOut("/**\n * @constructor\n * @extends {C}\n */\nD = function() {\n D.base(this, 'constructor');\n};\ngoog.inherits(D, C);\n\n\n/**\n * @override\n */\nD.prototype.s = function() {\n return null;\n};"); + } + + @Test + public void testInferReturnWithOverrideAndSuperDeclaredReturn() + { + IFileNode node = compileAS( + "package { public class A {} }\n" + + "class C { public function s():String { return null; } }\n" + + "class D extends C { override public function s() { return null; } }"); + IClassNode d = null; + for(int i = 0; i < node.getChildCount(); i++) + { + IASNode child = node.getChild(i); + if (child instanceof IClassNode) + { + IClassNode childClass = (IClassNode) child; + if ("D".equals(childClass.getName())) + { + d = childClass; + break; + } + } + } + asBlockWalker.visitClass(d); + // overrides don't need to specify type + assertOut("/**\n * @constructor\n * @extends {C}\n */\nD = function() {\n D.base(this, 'constructor');\n};\ngoog.inherits(D, C);\n\n\n/**\n * @override\n */\nD.prototype.s = function() {\n return null;\n};"); + } + + // ----- member variables + + @Test + public void testInferMemberVariableWithStringDefaultValue() + { + IVariableNode node = getField("public var s = \"hello\""); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @type {string}\n */\nRoyaleTest_A.prototype.s = \"hello\""); + } + + @Test + public void testInferMemberVariableWithNullDefaultValue() + { + IVariableNode node = getField("public var s = null"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @type {*}\n */\nRoyaleTest_A.prototype.s = null"); + } + + @Test + public void testInferMemberVariableWithUndefinedDefaultValue() + { + IVariableNode node = getField("public var s = undefined"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @type {*}\n */\nRoyaleTest_A.prototype.s = undefined"); + } + + @Test + public void testInferMemberVariableWithNoDefaultValue() + { + IVariableNode node = getField("public var s"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @type {*}\n */\nRoyaleTest_A.prototype.s = undefined"); + } + + // ----- static variables + + @Test + public void testInferStaticVariableWithStringDefaultValue() + { + IVariableNode node = getField("public static var s = \"hello\""); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @nocollapse\n * @type {string}\n */\nRoyaleTest_A.s = \"hello\""); + } + + @Test + public void testInferStaticVariableWithNullDefaultValue() + { + IVariableNode node = getField("public static var s = null"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @nocollapse\n * @type {*}\n */\nRoyaleTest_A.s = null"); + } + + @Test + public void testInferStaticVariableWithUndefinedDefaultValue() + { + IVariableNode node = getField("public static var s = undefined"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @nocollapse\n * @type {*}\n */\nRoyaleTest_A.s = undefined"); + } + + @Test + public void testInferStaticVariableWithNoDefaultValue() + { + IVariableNode node = getField("public static var s"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @nocollapse\n * @type {*}\n */\nRoyaleTest_A.s = undefined"); + } + + // ----- static constants + + @Test + public void testInferStaticConstantWithStringDefaultValue() + { + IVariableNode node = getField("public static const s = \"hello\""); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @nocollapse\n * @const\n * @type {string}\n */\nRoyaleTest_A.s = \"hello\""); + } + + @Test + public void testInferStaticConstantWithNullDefaultValue() + { + IVariableNode node = getField("public static const s = null"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @nocollapse\n * @const\n * @type {*}\n */\nRoyaleTest_A.s = null"); + } + + @Test + public void testInferStaticConstantWithUndefinedDefaultValue() + { + IVariableNode node = getField("public static const s = undefined"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @nocollapse\n * @const\n * @type {*}\n */\nRoyaleTest_A.s = undefined"); + } + + @Test + public void testInferStaticConstantWithNoDefaultValue() + { + IVariableNode node = getField("public static const s"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @nocollapse\n * @const\n * @type {*}\n */\nRoyaleTest_A.s = undefined"); + } + + // ----- member constants + + @Test + public void testInferMemberConstantWithStringDefaultValue() + { + IVariableNode node = getField("public const s = \"hello\""); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @const\n * @type {string}\n */\nRoyaleTest_A.prototype.s = \"hello\""); + } + + @Test + public void testInferMemberConstantWithNullDefaultValue() + { + IVariableNode node = getField("public const s = null"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @const\n * @type {*}\n */\nRoyaleTest_A.prototype.s = null"); + } + + @Test + public void testInferMemberConstantWithUndefinedDefaultValue() + { + IVariableNode node = getField("public const s = undefined"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @const\n * @type {*}\n */\nRoyaleTest_A.prototype.s = undefined"); + } + + @Test + public void testInferMemberConstantWithNoDefaultValue() + { + IVariableNode node = getField("public const s"); + asBlockWalker.visitVariable(node); + assertOut("/**\n * @const\n * @type {*}\n */\nRoyaleTest_A.prototype.s = undefined"); + } + + // ----- local constants + + @Test + public void testInferLocalConstantWithStringDefaultValue() + { + IVariableNode node = getVariable("const s = \"hello\""); + asBlockWalker.visitVariable(node); + assertOut("\n/**\n * @const\n * @type {string}\n */\nvar s = \"hello\""); + } + + @Test + public void testInferLocalConstantWithNullDefaultValue() + { + IVariableNode node = getVariable("const s = null"); + asBlockWalker.visitVariable(node); + assertOut("\n/**\n * @const\n * @type {*}\n */\nvar s = null"); + } + + @Test + public void testInferLocalConstantWithUndefinedDefaultValue() + { + IVariableNode node = getVariable("const s = undefined"); + asBlockWalker.visitVariable(node); + assertOut("\n/**\n * @const\n * @type {*}\n */\nvar s = undefined"); + } + + @Test + public void testInferLocalConstantWithNoDefaultValue() + { + IVariableNode node = getVariable("const s"); + asBlockWalker.visitVariable(node); + assertOut("\n/**\n * @const\n * @type {*}\n */\nvar s"); + } + + // ----- getters and setters + + @Test + public void testInferGetterWithStringDefaultValue() + { + IGetterNode node = (IGetterNode) getAccessor("public function get s() { return \"hello\"; }"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @return {string}\n */\nRoyaleTest_A.prototype.s = function() {\n return \"hello\";\n}"); + } + + @Test + public void testInferGetterWithNullDefaultValue() + { + IGetterNode node = (IGetterNode) getAccessor("public function get s() { return null; }"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @return {*}\n */\nRoyaleTest_A.prototype.s = function() {\n return null;\n}"); + } + + @Test + public void testInferGetterWithUndefinedDefaultValue() + { + IGetterNode node = (IGetterNode) getAccessor("public function get s() { return undefined; }"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @return {*}\n */\nRoyaleTest_A.prototype.s = function() {\n return undefined;\n}"); + } + + @Test + public void testInferGetterWithSetterDeclaredType() + { + IClassNode node = getClassNode("class C { public function get s() { return null; } public function set s(value:String):void {} }"); + asBlockWalker.visitClass(node); + assertOut("/**\n * @constructor\n */\nC = function() {\n};\n\n\n/**\n * @nocollapse\n * @export\n * @type {string}\n */\nC.prototype.s;\n\n\nC.prototype.get__s = function() {\n return null;\n};\n\n\nC.prototype.set__s = function(value) {\n};\n\n\nObject.defineProperties(C.prototype, /** @lends {C.prototype} */ {\n/**\n * @type {string}\n */\ns: {\nget: C.prototype.get__s,\nset: C.prototype.set__s}}\n);"); + } + + @Test + public void testInferSetterWithGetterDeclaredType() + { + IClassNode node = getClassNode("class C { public function get s():String { return null; } public function set s(value):void {} }"); + asBlockWalker.visitClass(node); + assertOut("/**\n * @constructor\n */\nC = function() {\n};\n\n\n/**\n * @nocollapse\n * @export\n * @type {string}\n */\nC.prototype.s;\n\n\nC.prototype.get__s = function() {\n return null;\n};\n\n\nC.prototype.set__s = function(value) {\n};\n\n\nObject.defineProperties(C.prototype, /** @lends {C.prototype} */ {\n/**\n * @type {string}\n */\ns: {\nget: C.prototype.get__s,\nset: C.prototype.set__s}}\n);"); + } + + @Test + public void testInferSetterWithGetterStringDefaultValue() + { + IClassNode node = getClassNode("class C { public function get s() { return \"hello\"; } public function set s(value):void {} }"); + asBlockWalker.visitClass(node); + assertOut("/**\n * @constructor\n */\nC = function() {\n};\n\n\n/**\n * @nocollapse\n * @export\n * @type {string}\n */\nC.prototype.s;\n\n\nC.prototype.get__s = function() {\n return \"hello\";\n};\n\n\nC.prototype.set__s = function(value) {\n};\n\n\nObject.defineProperties(C.prototype, /** @lends {C.prototype} */ {\n/**\n * @type {string}\n */\ns: {\nget: C.prototype.get__s,\nset: C.prototype.set__s}}\n);"); + } + + @Test + public void testInferSetterWithNoDefaultValue() + { + ISetterNode node = (ISetterNode) getAccessor("public function set s(value) {}"); + asBlockWalker.visitFunction(node); + assertOut("/**\n * @param {*} value\n */\nRoyaleTest_A.prototype.s = function(value) {\n}"); + } + + @Test + public void testInferGetterWithOverrideAndSuperInferredReturn() + { + IFileNode node = compileAS( + "package { public class A {} }\n" + + "class C { public function get s() { return \"hello\"; } }\n" + + "class D extends C { override public function get s() { return null; } }"); + IClassNode d = null; + for(int i = 0; i < node.getChildCount(); i++) + { + IASNode child = node.getChild(i); + if (child instanceof IClassNode) + { + IClassNode childClass = (IClassNode) child; + if ("D".equals(childClass.getName())) + { + d = childClass; + break; + } + } + } + asBlockWalker.visitClass(d); + assertOut("/**\n * @constructor\n * @extends {C}\n */\nD = function() {\n D.base(this, 'constructor');\n};\ngoog.inherits(D, C);\n\n\nD.prototype.get__s = function() {\n return null;\n};\n\n\nObject.defineProperties(D.prototype, /** @lends {D.prototype} */ {\n/**\n * @type {string}\n */\ns: {\nget: D.prototype.get__s}}\n);"); + } + + @Test + public void testInferGetterWithOverrideAndSuperDeclaredReturn() + { + IFileNode node = compileAS( + "package { public class A {} }\n" + + "class C { public function get s():String { return null; } }\n" + + "class D extends C { override public function get s() { return null; } }"); + IClassNode d = null; + for(int i = 0; i < node.getChildCount(); i++) + { + IASNode child = node.getChild(i); + if (child instanceof IClassNode) + { + IClassNode childClass = (IClassNode) child; + if ("D".equals(childClass.getName())) + { + d = childClass; + break; + } + } + } + asBlockWalker.visitClass(d); + assertOut("/**\n * @constructor\n * @extends {C}\n */\nD = function() {\n D.base(this, 'constructor');\n};\ngoog.inherits(D, C);\n\n\nD.prototype.get__s = function() {\n return null;\n};\n\n\nObject.defineProperties(D.prototype, /** @lends {D.prototype} */ {\n/**\n * @type {string}\n */\ns: {\nget: D.prototype.get__s}}\n);"); + } + + @Test + public void testInferSetterWithOverrideAndSuperInferredType() + { + IFileNode node = compileAS( + "package { public class A {} }\n" + + "class C { public function get s():String {} public function set s(p) {} }\n" + + "class D extends C { override public function set s(p) {} }"); + IClassNode d = null; + for(int i = 0; i < node.getChildCount(); i++) + { + IASNode child = node.getChild(i); + if (child instanceof IClassNode) + { + IClassNode childClass = (IClassNode) child; + if ("D".equals(childClass.getName())) + { + d = childClass; + break; + } + } + } + asBlockWalker.visitClass(d); + assertOut("/**\n * @constructor\n * @extends {C}\n */\nD = function() {\n D.base(this, 'constructor');\n};\ngoog.inherits(D, C);\n\n\nD.prototype.set__s = function(p) {\n};\n\n\nObject.defineProperties(D.prototype, /** @lends {D.prototype} */ {\n/**\n * @type {string}\n */\ns: {\nget: C.prototype.get__s,\nset: D.prototype.set__s}}\n);"); + } + + @Test + public void testInferSetterWithOverrideAndSuperDeclaredType() + { + IFileNode node = compileAS( + "package { public class A {} }\n" + + "class C { public function set s(p:String) {} }\n" + + "class D extends C { override public function set s(p) {} }"); + IClassNode d = null; + for(int i = 0; i < node.getChildCount(); i++) + { + IASNode child = node.getChild(i); + if (child instanceof IClassNode) + { + IClassNode childClass = (IClassNode) child; + if ("D".equals(childClass.getName())) + { + d = childClass; + break; + } + } + } + asBlockWalker.visitClass(d); + assertOut("/**\n * @constructor\n * @extends {C}\n */\nD = function() {\n D.base(this, 'constructor');\n};\ngoog.inherits(D, C);\n\n\nD.prototype.set__s = function(p) {\n};\n\n\nObject.defineProperties(D.prototype, /** @lends {D.prototype} */ {\n/**\n * @type {string}\n */\ns: {\nset: D.prototype.set__s}}\n);"); + } + + protected IBackend createBackend() + { + return new RoyaleBackend(); + } +} \ No newline at end of file diff --git a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/ASTestBase.java b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/ASTestBase.java index 807c950c4..5c8116b46 100644 --- a/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/ASTestBase.java +++ b/compiler-jx/src/test/java/org/apache/royale/compiler/internal/test/ASTestBase.java @@ -26,6 +26,7 @@ import org.apache.royale.compiler.internal.driver.as.ASBackend; import org.apache.royale.compiler.tree.as.IASNode; import org.apache.royale.compiler.tree.as.IAccessorNode; import org.apache.royale.compiler.tree.as.IBinaryOperatorNode; +import org.apache.royale.compiler.tree.as.IClassNode; import org.apache.royale.compiler.tree.as.IDynamicAccessNode; import org.apache.royale.compiler.tree.as.IExpressionNode; import org.apache.royale.compiler.tree.as.IFileNode; @@ -134,6 +135,12 @@ public class ASTestBase extends TestBase return findFirstDescendantOfType(node, type); } + protected IClassNode getClassNode(String code) + { + return (IClassNode) getNode(code, IClassNode.class, + WRAP_LEVEL_PACKAGE); + } + protected IInterfaceNode getInterfaceNode(String code) { return (IInterfaceNode) getNode(code, IInterfaceNode.class, diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ABCGeneratingReducer.java b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ABCGeneratingReducer.java index b4eb1cff8..42209b662 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ABCGeneratingReducer.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/as/codegen/ABCGeneratingReducer.java @@ -89,6 +89,7 @@ import org.apache.royale.compiler.tree.as.IScopedNode; import org.apache.royale.compiler.tree.as.ITryNode; import org.apache.royale.compiler.tree.as.ITypedExpressionNode; import org.apache.royale.compiler.tree.as.IUnaryOperatorNode; +import org.apache.royale.compiler.tree.as.IVariableNode; import org.apache.royale.compiler.tree.as.IWhileLoopNode; import org.apache.royale.compiler.tree.as.IWithNode; import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode.LanguageIdentifierKind; @@ -5542,9 +5543,30 @@ public class ABCGeneratingReducer final Nsset qualifiers = SemanticUtils.getOpenNamespaces(iNode, project); final Name name = new Name(CONSTANT_Multiname, qualifiers, null); result = new Binding(iNode, name, identifier.resolve(project)); + return result; } else { + if (currentScope.getProject().getInferTypes() + && identifier.isImplicit() + && identifier.getName().equals(IASLanguageConstants.ANY_TYPE)) + { + IASNode parentNode = identifier.getParent(); + if (parentNode instanceof IVariableNode) + { + IVariableNode varNode = (IVariableNode) parentNode; + if (identifier.equals(varNode.getVariableTypeNode())) + { + ITypeDefinition typeDef = SemanticUtils.resolveVariableInferredType(varNode, currentScope.getProject()); + if (typeDef != null) + { + final Name name = new Name(new Namespace(CONSTANT_PackageNs, typeDef.getPackageName()), typeDef.getBaseName()); + result = new Binding(iNode, name, typeDef); + return result; + } + } + } + } result = currentScope.resolveName(identifier); currentScope.getMethodBodySemanticChecker().checkSimpleName(iNode, result); } diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/AccessorDefinition.java b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/AccessorDefinition.java index f842087b6..286bfac3d 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/AccessorDefinition.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/AccessorDefinition.java @@ -21,7 +21,6 @@ package org.apache.royale.compiler.internal.definitions; import java.util.Iterator; - import org.apache.royale.compiler.definitions.IAccessorDefinition; import org.apache.royale.compiler.definitions.IClassDefinition; import org.apache.royale.compiler.definitions.IDefinition; @@ -30,16 +29,16 @@ import org.apache.royale.compiler.definitions.IGetterDefinition; import org.apache.royale.compiler.definitions.IInterfaceDefinition; import org.apache.royale.compiler.definitions.INamespaceDefinition; import org.apache.royale.compiler.definitions.IPackageDefinition; +import org.apache.royale.compiler.definitions.IScopedDefinition; import org.apache.royale.compiler.definitions.ISetterDefinition; +import org.apache.royale.compiler.definitions.references.INamespaceReference; +import org.apache.royale.compiler.internal.as.codegen.BindableHelper; +import org.apache.royale.compiler.internal.scopes.ASScope; import org.apache.royale.compiler.problems.DuplicateFunctionDefinitionProblem; import org.apache.royale.compiler.problems.UnresolvedNamespaceProblem; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.scopes.IDefinitionSet; import org.apache.royale.compiler.tree.as.IVariableNode; -import org.apache.royale.compiler.definitions.IScopedDefinition; -import org.apache.royale.compiler.definitions.references.INamespaceReference; -import org.apache.royale.compiler.internal.as.codegen.BindableHelper; -import org.apache.royale.compiler.internal.scopes.ASScope; /** * {@code AccessorDefinition} is the abstract base class for definitions that diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/FunctionDefinition.java b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/FunctionDefinition.java index 562b4d606..f9e41b460 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/FunctionDefinition.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/FunctionDefinition.java @@ -35,8 +35,10 @@ import org.apache.royale.compiler.definitions.references.INamespaceReference; import org.apache.royale.compiler.definitions.metadata.IMetaTag; import org.apache.royale.compiler.definitions.metadata.IMetaTagAttribute; import org.apache.royale.compiler.definitions.references.IReference; +import org.apache.royale.compiler.definitions.references.ReferenceFactory; import org.apache.royale.compiler.internal.definitions.metadata.MetaTag; import org.apache.royale.compiler.internal.projects.CompilerProject; +import org.apache.royale.compiler.internal.semantics.SemanticUtils; import org.apache.royale.compiler.problems.ConflictingDefinitionProblem; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.scopes.IASScope; @@ -194,6 +196,21 @@ public class FunctionDefinition extends ScopedDefinitionBase implements IFunctio return null; } + if (project.getInferTypes() && getReturnTypeReference() == null) + { + IFunctionNode funcNode = (IFunctionNode) getNode(); + if (funcNode != null) + { + ITypeDefinition inferredReturnType = SemanticUtils.resolveFunctionInferredReturnType(funcNode, project); + if (inferredReturnType != null) + { + setReturnTypeReference(ReferenceFactory.resolvedReference(inferredReturnType)); + DependencyType dt = DependencyType.SIGNATURE; + return resolveType(returnTypeReference, project, dt); + } + } + } + // TODO We don't really need to make this a signature dependency // if this function is a function closure. If this function // is a closure then we could make this an expression dependency diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/GetterDefinition.java b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/GetterDefinition.java index 7139e3da0..b9bb2dafc 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/GetterDefinition.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/GetterDefinition.java @@ -22,9 +22,13 @@ package org.apache.royale.compiler.internal.definitions; import org.apache.royale.compiler.constants.IASKeywordConstants; import org.apache.royale.compiler.definitions.IGetterDefinition; import org.apache.royale.compiler.definitions.ISetterDefinition; +import org.apache.royale.compiler.definitions.ITypeDefinition; import org.apache.royale.compiler.definitions.metadata.IMetaTag; +import org.apache.royale.compiler.definitions.references.ReferenceFactory; +import org.apache.royale.compiler.internal.semantics.SemanticUtils; import org.apache.royale.compiler.problems.IncompatibleOverrideProblem; import org.apache.royale.compiler.projects.ICompilerProject; +import org.apache.royale.compiler.tree.as.IGetterNode; public class GetterDefinition extends AccessorDefinition implements IGetterDefinition { @@ -116,4 +120,26 @@ public class GetterDefinition extends AccessorDefinition implements IGetterDefin return override; } + + @Override + public TypeDefinitionBase resolveType(ICompilerProject project) + { + if (project.getInferTypes() && getTypeReference() == null) + { + IGetterNode getterNode = (IGetterNode) getNode(); + ITypeDefinition typeDef = SemanticUtils.resolveFunctionInferredReturnType(getterNode, project); + if (typeDef != null) + { + setTypeReference(ReferenceFactory.resolvedReference(typeDef)); + return (TypeDefinitionBase) typeDef; + } + } + return super.resolveType(project); + } + + @Override + public ITypeDefinition resolveReturnType(ICompilerProject project) + { + return resolveType(project); + } } diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/ParameterDefinition.java b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/ParameterDefinition.java index 25cd2f979..e76b90691 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/ParameterDefinition.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/ParameterDefinition.java @@ -22,9 +22,11 @@ package org.apache.royale.compiler.internal.definitions; import org.apache.royale.compiler.common.DependencyType; import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType; import org.apache.royale.compiler.definitions.IParameterDefinition; +import org.apache.royale.compiler.definitions.ITypeDefinition; import org.apache.royale.compiler.definitions.references.IReference; import org.apache.royale.compiler.definitions.references.ReferenceFactory; import org.apache.royale.compiler.internal.scopes.CatchScope; +import org.apache.royale.compiler.internal.semantics.SemanticUtils; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.scopes.IASScope; import org.apache.royale.compiler.tree.as.IParameterNode; @@ -75,6 +77,25 @@ public class ParameterDefinition extends VariableDefinition implements IParamete flags |= FLAG_DEFAULT; } + @Override + public TypeDefinitionBase resolveType(ICompilerProject project) + { + if (project.getInferTypes() && getTypeReference() == null) + { + IParameterNode paramNode = (IParameterNode) getNode(); + if (paramNode != null) + { + ITypeDefinition typeDef = SemanticUtils.resolveVariableInferredType(paramNode, project); + if (typeDef != null) + { + setTypeReference(ReferenceFactory.resolvedReference(typeDef)); + return (TypeDefinitionBase) typeDef; + } + } + } + return super.resolveType(project); + } + @Override public Object resolveDefaultValue(ICompilerProject project) { diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/SetterDefinition.java b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/SetterDefinition.java index 8d877afd3..01d4ef66b 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/SetterDefinition.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/SetterDefinition.java @@ -22,9 +22,13 @@ package org.apache.royale.compiler.internal.definitions; import org.apache.royale.compiler.constants.IASKeywordConstants; import org.apache.royale.compiler.definitions.IGetterDefinition; import org.apache.royale.compiler.definitions.ISetterDefinition; +import org.apache.royale.compiler.definitions.ITypeDefinition; import org.apache.royale.compiler.definitions.metadata.IMetaTag; +import org.apache.royale.compiler.definitions.references.ReferenceFactory; +import org.apache.royale.compiler.internal.semantics.SemanticUtils; import org.apache.royale.compiler.problems.IncompatibleOverrideProblem; import org.apache.royale.compiler.projects.ICompilerProject; +import org.apache.royale.compiler.tree.as.ISetterNode; public class SetterDefinition extends AccessorDefinition implements ISetterDefinition { @@ -115,4 +119,23 @@ public class SetterDefinition extends AccessorDefinition implements ISetterDefin return override; } + @Override + public TypeDefinitionBase resolveType(ICompilerProject project) + { + if (project.getInferTypes() && getTypeReference() == null) + { + ISetterNode setterNode = (ISetterNode) getNode(); + if (setterNode != null) + { + ITypeDefinition typeDef = SemanticUtils.resolveVariableInferredType(setterNode, project); + if (typeDef != null) + { + setTypeReference(ReferenceFactory.resolvedReference(typeDef)); + return (TypeDefinitionBase) typeDef; + } + } + } + return super.resolveType(project); + } + } diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/VariableDefinition.java b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/VariableDefinition.java index 2f8a1e90d..30ca50173 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/VariableDefinition.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/definitions/VariableDefinition.java @@ -25,8 +25,10 @@ import org.apache.royale.compiler.constants.IASKeywordConstants; import org.apache.royale.compiler.definitions.IClassDefinition; import org.apache.royale.compiler.definitions.IDefinition; import org.apache.royale.compiler.definitions.IPackageDefinition; +import org.apache.royale.compiler.definitions.ITypeDefinition; import org.apache.royale.compiler.definitions.IVariableDefinition; import org.apache.royale.compiler.definitions.metadata.IMetaTag; +import org.apache.royale.compiler.definitions.references.ReferenceFactory; import org.apache.royale.compiler.internal.as.codegen.CodeGeneratorManager; import org.apache.royale.compiler.internal.as.codegen.ICodeGenerator; import org.apache.royale.compiler.internal.as.codegen.ICodeGenerator.IConstantValue; @@ -226,6 +228,25 @@ public class VariableDefinition extends DefinitionBase implements IVariableDefin } } + @Override + public TypeDefinitionBase resolveType(ICompilerProject project) + { + if (project.getInferTypes() && getTypeReference() == null) + { + IVariableNode varNode = (IVariableNode) getNode(); + if (varNode != null) + { + ITypeDefinition typeDef = SemanticUtils.resolveVariableInferredType(varNode, project); + if (typeDef != null) + { + setTypeReference(ReferenceFactory.resolvedReference(typeDef)); + return (TypeDefinitionBase) typeDef; + } + } + } + return super.resolveType(project); + } + public IExpressionNode getInitializer() { return initializer; } diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/ConfigProcessor.java b/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/ConfigProcessor.java index ada8f9466..6f8c7a21c 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/ConfigProcessor.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/parsing/as/ConfigProcessor.java @@ -182,6 +182,12 @@ public class ConfigProcessor // TODO Auto-generated method stub return false; } + + @Override + public boolean getInferTypes() { + // TODO Auto-generated method stub + return false; + } } /** diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/ASCProject.java b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/ASCProject.java index f37c32da8..6e408f508 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/ASCProject.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/ASCProject.java @@ -104,4 +104,10 @@ public class ASCProject extends CompilerProject implements IASCProject // TODO Auto-generated method stub return false; } + + @Override + public boolean getInferTypes() { + // TODO Auto-generated method stub + return false; + } } diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProject.java b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProject.java index a97354f5e..6f29509ee 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProject.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProject.java @@ -2528,6 +2528,21 @@ public class RoyaleProject extends ASProject implements IRoyaleProject, ICompile strictIdentifierNames = enabled; } + private boolean inferTypes = false; + + /** + * Indicates if type inference is enabled. + */ + @Override + public boolean getInferTypes() + { + return inferTypes; + } + public void setInferTypes(boolean enabled) + { + inferTypes = enabled; + } + @Override public boolean isPlatformRule(ICSSRule rule) { return true; diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java index 2eca5f9fd..7e08de4eb 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/projects/RoyaleProjectConfigurator.java @@ -273,6 +273,8 @@ public class RoyaleProjectConfigurator extends Configurator project.setAllowPrivateConstructors(configuration.getCompilerAllowPrivateConstructors()); project.setStrictIdentifierNames(configuration.getCompilerStrictIdentifierNames()); + + project.setInferTypes(configuration.getInferTypes()); project.setSwfDebugfileAlias(configuration.getSwfDebugfileAlias()); if (configuration.getSwfDebugfileAlias() != null) diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/MethodBodySemanticChecker.java b/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/MethodBodySemanticChecker.java index f76d19b17..c8adb1221 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/MethodBodySemanticChecker.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/MethodBodySemanticChecker.java @@ -2623,6 +2623,14 @@ public class MethodBodySemanticChecker ///////////////////////////////////// // Check for a type on the variable declaration String type = var.getTypeName(); + if (project.getInferTypes() && type.isEmpty() && !var.hasExplicitType()) + { + ITypeDefinition inferredType = SemanticUtils.resolveVariableInferredType(var, project); + if (inferredType != null) + { + type = inferredType.getQualifiedName(); + } + } if (type.isEmpty()) // empty string means no declaration at all (not *) { diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/SemanticUtils.java b/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/SemanticUtils.java index 120b934c8..bf013d4a1 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/SemanticUtils.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/semantics/SemanticUtils.java @@ -22,6 +22,7 @@ package org.apache.royale.compiler.internal.semantics; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -29,12 +30,25 @@ import org.apache.royale.abc.ABCConstants; import org.apache.royale.abc.semantics.Name; import org.apache.royale.abc.semantics.Namespace; import org.apache.royale.abc.semantics.Nsset; +import org.apache.royale.compiler.common.ASModifier; import org.apache.royale.compiler.common.DependencyType; import org.apache.royale.compiler.constants.IASKeywordConstants; import org.apache.royale.compiler.constants.IASLanguageConstants; import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType; -import org.apache.royale.compiler.definitions.*; +import org.apache.royale.compiler.definitions.IAccessorDefinition; +import org.apache.royale.compiler.definitions.IClassDefinition; +import org.apache.royale.compiler.definitions.IConstantDefinition; +import org.apache.royale.compiler.definitions.IDefinition; +import org.apache.royale.compiler.definitions.IFunctionDefinition; +import org.apache.royale.compiler.definitions.IGetterDefinition; import org.apache.royale.compiler.definitions.IFunctionDefinition.FunctionClassification; +import org.apache.royale.compiler.definitions.IInterfaceDefinition; +import org.apache.royale.compiler.definitions.INamespaceDefinition; +import org.apache.royale.compiler.definitions.IParameterDefinition; +import org.apache.royale.compiler.definitions.IScopedDefinition; +import org.apache.royale.compiler.definitions.ISetterDefinition; +import org.apache.royale.compiler.definitions.ITypeDefinition; +import org.apache.royale.compiler.definitions.IVariableDefinition; import org.apache.royale.compiler.definitions.metadata.IDeprecationInfo; import org.apache.royale.compiler.definitions.references.INamespaceReference; import org.apache.royale.compiler.definitions.references.IReference; @@ -103,8 +117,35 @@ import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.scopes.IASScope; import org.apache.royale.compiler.scopes.IDefinitionSet; import org.apache.royale.compiler.tree.ASTNodeID; -import org.apache.royale.compiler.tree.as.*; +import org.apache.royale.compiler.tree.as.IASNode; +import org.apache.royale.compiler.tree.as.IBinaryOperatorNode; +import org.apache.royale.compiler.tree.as.IClassNode; +import org.apache.royale.compiler.tree.as.ICommonClassNode; +import org.apache.royale.compiler.tree.as.IContainerNode; +import org.apache.royale.compiler.tree.as.IDefinitionNode; +import org.apache.royale.compiler.tree.as.IDynamicAccessNode; +import org.apache.royale.compiler.tree.as.IExpressionNode; +import org.apache.royale.compiler.tree.as.IFileNode; +import org.apache.royale.compiler.tree.as.IFunctionCallNode; +import org.apache.royale.compiler.tree.as.IFunctionNode; +import org.apache.royale.compiler.tree.as.IGetterNode; +import org.apache.royale.compiler.tree.as.IIdentifierNode; +import org.apache.royale.compiler.tree.as.IImportNode; +import org.apache.royale.compiler.tree.as.IInterfaceNode; +import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode; import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode.LanguageIdentifierKind; +import org.apache.royale.compiler.tree.as.ILiteralNode; +import org.apache.royale.compiler.tree.as.IMemberAccessExpressionNode; +import org.apache.royale.compiler.tree.as.INamespaceDecorationNode; +import org.apache.royale.compiler.tree.as.INumericLiteralNode; +import org.apache.royale.compiler.tree.as.IParameterNode; +import org.apache.royale.compiler.tree.as.IReturnNode; +import org.apache.royale.compiler.tree.as.IScopedNode; +import org.apache.royale.compiler.tree.as.ISetterNode; +import org.apache.royale.compiler.tree.as.ITryNode; +import org.apache.royale.compiler.tree.as.ITypeNode; +import org.apache.royale.compiler.tree.as.IUnaryOperatorNode; +import org.apache.royale.compiler.tree.as.IVariableNode; import org.apache.royale.compiler.tree.mxml.IMXMLEventSpecifierNode; /** @@ -2731,7 +2772,15 @@ public class SemanticUtils ILanguageIdentifierNode identifier = (ILanguageIdentifierNode) paramType; if (identifier.getKind().equals(LanguageIdentifierKind.ANY_TYPE)) { - scope.addProblem(new ParameterHasNoTypeDeclarationProblem(paramNode, paramNode.getName(), node.getName())); + ITypeDefinition inferredTypeDefinition = null; + if (scope.getProject().getInferTypes()) + { + inferredTypeDefinition = SemanticUtils.resolveVariableInferredType(paramNode, scope.getProject()); + } + if (inferredTypeDefinition == null) + { + scope.addProblem(new ParameterHasNoTypeDeclarationProblem(paramNode, paramNode.getName(), node.getName())); + } } } } @@ -2757,9 +2806,14 @@ public class SemanticUtils return; IExpressionNode returnType = node.getReturnTypeNode(); + ITypeDefinition inferredReturnTypeDef = null; + if (scope.getProject().getInferTypes() && returnType == null && func != null) + { + inferredReturnTypeDef = SemanticUtils.resolveFunctionInferredReturnType(node, scope.getProject()); + } // check for return type declaration - if (returnType == null) + if (returnType == null && inferredReturnTypeDef == null) { // if none found, derive best source location for problem report IASNode sourceLoc = node; @@ -3251,4 +3305,362 @@ public class SemanticUtils } return file_name; } + + public static ITypeDefinition resolveVariableInferredType(IVariableNode iNode, ICompilerProject project) + { + if (iNode instanceof IGetterNode) + { + IGetterNode getterNode = (IGetterNode) iNode; + return resolveFunctionInferredReturnType(getterNode, project); + } + ISetterDefinition setterDef = null; + if (iNode instanceof ISetterNode) + { + ISetterNode setterNode = (ISetterNode) iNode; + setterDef = (ISetterDefinition) setterNode.getDefinition(); + } + else if (iNode instanceof IParameterNode) + { + // if we have a parameter, check if it belongs to a setter function + // we can use the setter to resolve the type + IParameterNode paramNode = (IParameterNode) iNode; + IASNode parentNode = paramNode.getParent(); + if (parentNode != null) + { + IASNode gpNode = parentNode.getParent(); + if (gpNode instanceof ISetterNode) + { + ISetterNode setterNode = (ISetterNode) gpNode; + setterDef = (ISetterDefinition) setterNode.getDefinition(); + } + } + } + if (setterDef != null) + { + // setters won't have an assigned value to infer from, but we + // could either infer from the super setter (if the setter is an + // override), or from a corresponding getter + ISetterDefinition overriddenSetterDef = (ISetterDefinition) setterDef.resolveOverriddenFunction(project); + if (overriddenSetterDef != null) + { + ITypeDefinition resolvedType = overriddenSetterDef.resolveType(project); + if (resolvedType != null && getBuiltinType(BuiltinType.ANY_TYPE, project).equals(resolvedType)) + { + // see note below about the * type + return null; + } + return resolvedType; + } + else + { + IGetterDefinition getterDef = setterDef.resolveGetter(project); + if (getterDef != null) + { + ITypeDefinition resolvedType = getterDef.resolveReturnType(project); + if (resolvedType != null && getBuiltinType(BuiltinType.ANY_TYPE, project).equals(resolvedType)) + { + // see note below about the * type + return null; + } + return resolvedType; + } + } + } + IExpressionNode assignedValueNode = iNode.getAssignedValueNode(); + if (assignedValueNode == null) + { + // nothing to infer a type from + return null; + } + if (ASTNodeID.LiteralNullID.equals(assignedValueNode.getNodeID())) + { + // we can't infer a specific type from null + return null; + } + ITypeDefinition resolvedType = assignedValueNode.resolveType(project); + if (getBuiltinType(BuiltinType.ANY_TYPE, project).equals(resolvedType)) + { + // users should still get a missing type warning for the * type. + // if they want to get rid of the warning, they should explicitly + // declare the * type. + // keeping the missing type warning for * encourages the use of more + // specific types, and ensures that * is used only when needed. + return null; + } + return resolvedType; + } + + public static ITypeDefinition resolveFunctionInferredReturnType(IFunctionNode iNode, ICompilerProject project) + { + if (iNode.hasModifier(ASModifier.OVERRIDE)) + { + // if this is an override, resolve the return type from the original + IFunctionDefinition funcDef = (IFunctionDefinition) iNode.getDefinition(); + if (funcDef != null) + { + IFunctionDefinition overridenFuncDef = funcDef.resolveOverriddenFunction(project); + if (overridenFuncDef != null) + { + return overridenFuncDef.resolveReturnType(project); + } + } + } + if (iNode instanceof IGetterNode) + { + // first, try to find an explicit parameter type from the setter + IGetterNode getterNode = (IGetterNode) iNode; + IGetterDefinition getterDef = (IGetterDefinition) getterNode.getDefinition(); + if (getterDef != null) + { + ISetterDefinition setterDef = getterDef.resolveSetter(project); + if (setterDef != null) + { + ISetterNode setterNode = (ISetterNode) setterDef.getNode(); + if (setterNode != null) + { + IParameterNode[] paramNodes = setterNode.getParameterNodes(); + if (paramNodes != null && paramNodes.length > 0) + { + IParameterNode firstSetterParamNode = paramNodes[0]; + IExpressionNode varTypeNode = firstSetterParamNode.getVariableTypeNode(); + if (varTypeNode != null) + { + boolean isImplicitAny = varTypeNode.getAbsoluteStart() == -1 + && varTypeNode.getAbsoluteEnd() == -1 + && varTypeNode instanceof ILanguageIdentifierNode + && ((ILanguageIdentifierNode) varTypeNode).getKind().equals(LanguageIdentifierKind.ANY_TYPE); + if (!isImplicitAny) + { + IDefinition typeDef = varTypeNode.resolve(project); + if (typeDef instanceof ITypeDefinition) + { + return (ITypeDefinition) typeDef; + } + } + } + } + } + } + } + } + if (iNode instanceof ISetterNode) + { + return project.getBuiltinType(IASLanguageConstants.BuiltinType.VOID); + } + if (iNode.hasModifier(ASModifier.ABSTRACT)) + { + // we can't infer the return type of an abstract method because it + // won't have a body + return null; + } + ITypeNode parentInterfaceNode = (IInterfaceNode) iNode.getAncestorOfType(IInterfaceNode.class); + if (parentInterfaceNode != null) + { + // we can't infer the return type of a method on an interface + // because it won't have a body + return null; + } + List<IReturnNode> returnNodes = new ArrayList<IReturnNode>(); + findReturnNodes(iNode, returnNodes); + ITypeDefinition foundTypeDef = null; + boolean hasReturnValueNode = false; + for(IReturnNode returnNode : returnNodes) + { + IExpressionNode returnValueNode = returnNode.getReturnValueNode(); + // nothing to infer a type from + if (returnValueNode == null || ASTNodeID.NilID.equals(returnValueNode.getNodeID())) + { + continue; + } + // we can't infer a specific type from null + if (ASTNodeID.LiteralNullID.equals(returnValueNode.getNodeID())) + { + hasReturnValueNode = true; + continue; + } + ITypeDefinition typeDef = returnNode.resolveType(project); + if (!hasReturnValueNode && foundTypeDef == null) + { + foundTypeDef = typeDef; + } + else + { + foundTypeDef = SemanticUtils.resolveCommonType(foundTypeDef, typeDef, project); + } + hasReturnValueNode = true; + } + if (foundTypeDef != null && !getBuiltinType(BuiltinType.ANY_TYPE, project).equals(foundTypeDef)) + { + // users should still get a missing type warning for the * type. + // if they want to get rid of the warning, they should explicitly + // declare the * type. + // keeping the missing type warning for * encourages the use of more + // specific types, and ensures that * is used only when needed. + return foundTypeDef; + } + if (!hasReturnValueNode && !(iNode instanceof IGetterNode)) + { + // there were either no return statements, or only ones without a value + return project.getBuiltinType(IASLanguageConstants.BuiltinType.VOID); + } + return null; + } + + private static void findReturnNodes(IASNode node, List<IReturnNode> result) + { + for(int i = 0; i < node.getChildCount(); i++) + { + IASNode child = node.getChild(i); + if (child instanceof IReturnNode) + { + result.add((IReturnNode)child); + continue; + } + if(child.isTerminal()) + { + continue; + } + findReturnNodes(child, result); + } + } + + /** + * Resolves a common interface. Checks if A == B, if A extends B, or + * if B extends A. Does not check if both A and B extend a common interface + * C because A and B could have multiple common interfaces, and there is no + * clear way to choose one of those common interfaces over another. + */ + public static IInterfaceDefinition resolveCommonType(IInterfaceDefinition interfaceA, IInterfaceDefinition interfaceB, ICompilerProject project) + { + if (interfaceA == null || interfaceB == null) + { + return null; + } + if (interfaceA.equals(interfaceB)) + { + return interfaceA; + } + Iterator<IInterfaceDefinition> interfacesA = interfaceA.interfaceIterator(project, true); + while (interfacesA.hasNext()) + { + IInterfaceDefinition a = interfacesA.next(); + if (interfaceB.equals(a)) + { + return interfaceB; + } + } + Iterator<IInterfaceDefinition> interfacesB = interfaceB.interfaceIterator(project, true); + while (interfacesB.hasNext()) + { + IInterfaceDefinition b = interfacesB.next(); + if (interfaceA.equals(b)) + { + return interfaceA; + } + } + return null; + } + + /** + * Checks if A implements B. Does not check if both A and B implement a + * common interface C because A and B could have multiple common interfaces, + * and there is no clear way to choose one of those common interfaces over + * another. + */ + public static IInterfaceDefinition resolveCommonBaseInterface(IClassDefinition classA, IInterfaceDefinition interfaceB, ICompilerProject project) + { + if (classA == null || interfaceB == null) + { + return null; + } + Iterator<IInterfaceDefinition> interfacesA = classA.interfaceIterator(project); + while (interfacesA.hasNext()) + { + IInterfaceDefinition a = interfacesA.next(); + if (interfaceB.equals(a)) + { + return interfaceB; + } + } + return null; + } + + /** + * Checks if B implements A. Does not check if both A and B implement a + * common interface C because A and B could have multiple common interfaces, + * and there is no clear way to choose one of those common interfaces over + * another. + */ + public static IInterfaceDefinition resolveCommonBaseInterface(IInterfaceDefinition interfaceA, IClassDefinition classB, ICompilerProject project) + { + return resolveCommonBaseInterface(classB, interfaceA, project); + } + + /** + * Resolves a common class. Checks if A == B, if A extends B, or + * if B extends A. Does not check if both A and B implement a common + * interface C because A and B could implement multiple common interfaces, + * and there is no clear way to choose one of those common interfaces over + * another. + */ + public static IClassDefinition resolveCommonType(IClassDefinition classA, IClassDefinition classB, ICompilerProject project) + { + if (classA == null || classB == null) + { + return null; + } + if (isNumericType(classA, project) + && isNumericType(classB, project) + && !classA.equals(classB)) { + return (IClassDefinition) getBuiltinType(BuiltinType.NUMBER, project); + } + Iterator<IClassDefinition> classesA = classA.classIterator(project, true); + while (classesA.hasNext()) + { + IClassDefinition a = classesA.next(); + Iterator<IClassDefinition> classesB = classB.classIterator(project, true); + while (classesB.hasNext()) + { + IClassDefinition b = classesB.next(); + if (a.equals(b)) + { + return a; + } + } + } + return null; + } + + /** + * Resolves a common type. Depending on whether each type is a class or an + * interface, checks if A == B, if A extends B, if B extends A, + * if A implements B, or if B implements A. Does not check if both A and B + * implement a common interface C because A and B could implement multiple + * common interfaces, and there is no clear way to choose one of those + * common interfaces over another. + */ + public static ITypeDefinition resolveCommonType(ITypeDefinition typeA, ITypeDefinition typeB, ICompilerProject project) + { + if (typeA == null || typeB == null) + { + return null; + } + if (typeA instanceof IClassDefinition && typeB instanceof IClassDefinition) + { + return resolveCommonType((IClassDefinition) typeA, (IClassDefinition) typeB, project); + } + if (typeA instanceof IInterfaceDefinition && typeB instanceof IInterfaceDefinition) + { + return resolveCommonType((IInterfaceDefinition) typeA, (IInterfaceDefinition) typeB, project); + } + if (typeA instanceof IClassDefinition && typeB instanceof IInterfaceDefinition) + { + return resolveCommonType((IClassDefinition) typeA, (IInterfaceDefinition) typeB, project); + } + if (typeA instanceof IInterfaceDefinition && typeB instanceof IClassDefinition) + { + return resolveCommonType((IClassDefinition) typeA, (IInterfaceDefinition) typeB, project); + } + return null; + } } diff --git a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/ReturnNode.java b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/ReturnNode.java index 674f48b47..c808b99a0 100644 --- a/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/ReturnNode.java +++ b/compiler/src/main/java/org/apache/royale/compiler/internal/tree/as/ReturnNode.java @@ -19,6 +19,8 @@ package org.apache.royale.compiler.internal.tree.as; +import org.apache.royale.compiler.constants.IASLanguageConstants.BuiltinType; +import org.apache.royale.compiler.definitions.ITypeDefinition; import org.apache.royale.compiler.parsing.IASToken; import org.apache.royale.compiler.projects.ICompilerProject; import org.apache.royale.compiler.tree.ASTNodeID; @@ -62,8 +64,17 @@ public class ReturnNode extends BaseStatementExpressionNode implements IReturnNo // // ExpressionNodeBase overrides // - - // TODO Does this class need to override resolveType()? + + @Override + public ITypeDefinition resolveType(ICompilerProject project) + { + IExpressionNode returnValueNode = getReturnValueNode(); + if (returnValueNode != null) + { + return returnValueNode.resolveType(project); + } + return null; + } @Override protected ReturnNode copy() diff --git a/compiler/src/test/java/as/ASFeatureTestsBase.java b/compiler/src/test/java/as/ASFeatureTestsBase.java index 3e6ff8ab1..d49084d15 100644 --- a/compiler/src/test/java/as/ASFeatureTestsBase.java +++ b/compiler/src/test/java/as/ASFeatureTestsBase.java @@ -188,6 +188,13 @@ public class ASFeatureTestsBase String results = compile(tempASFile, source, withFramework, withRPC, withSpark, otherOptions, false); assertThat(results, is(errors)); } + + protected void compileAndExpectNoErrors(String source, boolean withFramework, boolean withRPC, boolean withSpark, String[] otherOptions) + { + File tempASFile = generateTempFile(source); + String results = compile(tempASFile, source, withFramework, withRPC, withSpark, otherOptions, false); + assertThat(results, is("")); + } protected void compileAndRun(String source, boolean withFramework, boolean withRPC, boolean withSpark, String[] otherOptions) { diff --git a/compiler/src/test/java/as/ASInferTypesTests.java b/compiler/src/test/java/as/ASInferTypesTests.java new file mode 100644 index 000000000..20a60c214 --- /dev/null +++ b/compiler/src/test/java/as/ASInferTypesTests.java @@ -0,0 +1,1912 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package as; + +import java.io.File; + +import org.junit.Assert; +import org.junit.Test; + +public class ASInferTypesTests extends ASFeatureTestsBase +{ + // ----- local variables + + @Test + public void testLocalVariableHasNoTypeDeclarationWarning_withInferTypesDisabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "var s = \"hello\";" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipLocalVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for variable because String is inferred + "var s = \"hello\";" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testLocalVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueNull() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from null + "var s = null;" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testLocalVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefined() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from undefined + "var s = undefined;" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipLocalVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefinedAndDeclaredAnyType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for undefined when the * type is declared + "var s:* = undefined;" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + // ----- parameters + + @Test + public void testParameterHasNoTypeDeclarationWarning_withInferTypesDisabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "function f(s = \"hello\"):void {}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "parameter 's' for function 'f' has no type declaration.\n"); + } + + @Test + public void testSkipParameterHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for parameter because String is inferred + "function f(s = \"hello\"):void {}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testParameterHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueNull() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from null + "function f(s = null):void {}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "parameter 's' for function 'f' has no type declaration.\n"); + } + + @Test + public void testParameterHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefined() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from undefined + "function f(s = undefined):void {}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "parameter 's' for function 'f' has no type declaration.\n"); + } + + @Test + public void testSkipParameterHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefinedAndDeclaredAnyType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for undefined when the * type is declared + "function f(s:* = undefined):void {}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + // ----- returns + + @Test + public void testReturnValueHasNoTypeDeclarationWarning_withInferTypesDisabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "function f() { return \"hello\"; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 'f' has no type declaration.\n"); + } + + @Test + public void testSkipReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for return value because String is inferred + "function f() { return \"hello\"; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueNull() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from null + "function f() { return null; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 'f' has no type declaration.\n"); + } + + @Test + public void testReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefined() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from undefined + "function f() { return undefined; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 'f' has no type declaration.\n"); + } + + @Test + public void testSkipReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefinedAndDeclaredAnyType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from undefined + "function f():* { return undefined; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testReturnValueHasNoTypeDeclarationWarning_withInferTypesDisabledAndNoReturnStatements() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "function f() {}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 'f' has no type declaration.\n"); + } + + @Test + public void testSkipReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndNoReturnStatements() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no return statements means that void is inferred + "function f() {}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testReturnValueHasNoTypeDeclarationWarning_withInferTypesDisabledAndNoReturnValues() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "function f() { return; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 'f' has no type declaration.\n"); + } + + @Test + public void testSkipReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndNoReturnValues() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no return statements with values means that void is inferred + "function f() { return; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testReturnValueHasNoTypeDeclarationWarning_withInferTypesDisabledAndOverrideInferredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function f() { return \"hello\"; }", + "}", + "class D extends C {", + "override public function f() { return null; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 'f' has no type declaration.\nreturn value for function 'f' has no type declaration.\n"); + } + + @Test + public void testSkipReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndOverrideInferredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function f() { return \"hello\"; }", + "}", + "class D extends C {", + "override public function f() { return null; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testReturnValueHasNoTypeDeclarationWarning_withInferTypesDisabledAndOverrideDeclaredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function f():String { return null; }", + "}", + "class D extends C {", + "override public function f() { return null; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + + compileAndExpectErrors(source, false,false,false, options, "Incompatible override.\nreturn value for function 'f' has no type declaration.\n"); + } + + @Test + public void testSkipReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndOverrideDeclaredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function f():String { return null; }", + "}", + "class D extends C {", + "override public function f() { return null; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + // ----- member variables + + @Test + public void testMemberVariableHasNoTypeDeclarationWarning_withInferTypesDisabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C { public var s = \"hello\"; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipMemberVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for variable because String is inferred + "class C { public var s = \"hello\"; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testMemberVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueNull() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from null + "class C { public var s = null; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testMemberVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefined() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from undefined + "class C { public var s = undefined; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipMemberVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefinedAndDeclaredAnyType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for undefined when the * type is declared + "class C { public var s:* = undefined; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + // ----- static variables + + @Test + public void testStaticVariableHasNoTypeDeclarationWarning_withInferTypesDisabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C { public static var s = \"hello\"; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipStaticVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for variable because String is inferred + "class C { public static var s = \"hello\"; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testStaticVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueNull() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from null + "class C { public static var s = null; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testStaticVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefined() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from undefined + "class C { public static var s = undefined; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipStaticVariableHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefinedAndDeclaredAnyType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for undefined when the * type is declared + "class C { public static var s:* = undefined; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + // ----- static constants + + @Test + public void testStaticConstantHasNoTypeDeclarationWarning_withInferTypesDisabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C { public static const s = \"hello\"; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipStaticConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for variable because String is inferred + "class C { public static const s = \"hello\"; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testStaticConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueNull() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from null + "class C { public static const s = null; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testStaticConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefined() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from undefined + "class C { public static const s = undefined; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipStaticConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefinedAndDeclaredAnyType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for undefined when the * type is declared + "class C { public static const s:* = undefined; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + // ----- member constants + + @Test + public void testMemberConstantHasNoTypeDeclarationWarning_withInferTypesDisabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C { public const s = \"hello\"; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipMemberConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for variable because String is inferred + "class C { public const s = \"hello\"; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testMemberConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueNull() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from null + "class C { public const s = null; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testMemberConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefined() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from undefined + "class C { public const s = undefined; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipMemberConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefinedAndDeclaredAnyType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for undefined when the * type is declared + "class C { public const s:* = undefined; }" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + // ----- local constants + + @Test + public void testLocalConstantHasNoTypeDeclarationWarning_withInferTypesDisabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "const s = \"hello\";" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipLocalConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for variable because String is inferred + "const s = \"hello\";" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testLocalConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueNull() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from null + "const s = null;" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testLocalConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefined() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // can't infer a type from undefined + "const s = undefined;" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "variable 's' has no type declaration.\n"); + } + + @Test + public void testSkipLocalConstantHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefinedAndDeclaredAnyType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + // no warning for undefined when the * type is declared + "const s:* = undefined;" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + // ----- getters and setters + + @Test + public void testSetterParameterHasNoTypeDeclarationWarning_withInferTypesDisabledAndGetterDeclaredString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s():String { return null; }", + "public function set s(v):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "parameter 'v' for function 's' has no type declaration.\n"); + } + + @Test + public void testSkipSetterParameterHasNoTypeDeclarationWarning_withInferTypesEnabledAndGetterDeclaredString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s():String { return null; }", + "public function set s(v):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesDisabledAndSetterDeclaredString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return null; }", + "public function set s(v:String):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 's' has no type declaration.\n"); + } + + @Test + public void testSkipGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndSetterDeclaredString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return null; }", + "public function set s(v:String):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testGetterReturnValueHasNoTypeDeclarationWarningAndSetterParameterHasNoTypeDeclarationWarning_withInferTypesDisabledAndGetterDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return \"hello\"; }", + "public function set s(v):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 's' has no type declaration.\nparameter 'v' for function 's' has no type declaration.\n"); + } + + @Test + public void testSkipGetterReturnValueHasNoTypeDeclarationWarningAndSetterParameterHasNoTypeDeclarationWarning_withInferTypesEnabledAndGetterDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return \"hello\"; }", + "public function set s(v):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesDisabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return \"hello\"; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 's' has no type declaration.\n"); + } + + @Test + public void testSkipGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueString() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return \"hello\"; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueNull() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return null; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 's' has no type declaration.\n"); + } + + @Test + public void testGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefined() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return undefined; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 's' has no type declaration.\n"); + } + + @Test + public void testSkipGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndDefaultValueUndefinedAndDeclaredAnyType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s():* { return undefined; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesDisabledAndOverrideInferredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return \"hello\"; }", + "}", + "class D extends C {", + "override public function get s() { return null; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + compileAndExpectErrors(source, false,false,false, options, "return value for function 's' has no type declaration.\nreturn value for function 's' has no type declaration.\n"); + } + + @Test + public void testSkipGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndOverrideInferredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return \"hello\"; }", + "}", + "class D extends C {", + "override public function get s() { return null; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesDisabledAndOverrideDeclaredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s():String { return null; }", + "}", + "class D extends C {", + "override public function get s() { return null; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + + compileAndExpectErrors(source, false,false,false, options, "Incompatible override.\nreturn value for function 's' has no type declaration.\n"); + } + + @Test + public void testSkipGetterReturnValueHasNoTypeDeclarationWarning_withInferTypesEnabledAndOverrideDeclaredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s():String { return null; }", + "}", + "class D extends C {", + "override public function get s() { return null; }", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testSetterParameterHasNoTypeDeclarationWarning_withInferTypesDisabledAndOverrideDeclaredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function set s(p:String):void {}", + "}", + "class D extends C {", + "override public function set s(p):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + + compileAndExpectErrors(source, false,false,false, options, "Incompatible override.\nparameter 'p' for function 's' has no type declaration.\n"); + } + + @Test + public void testSkipSetterParameterHasNoTypeDeclarationWarning_withInferTypesEnabledAndOverrideDeclaredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function set s(p:String):void {}", + "}", + "class D extends C {", + "override public function set s(p):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testSetterParameterHasNoTypeDeclarationWarning_withInferTypesDisabledAndOverrideInferredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return \"hello\"; }", + "public function set s(p):void {}", + "}", + "class D extends C {", + "override public function set s(p):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + + compileAndExpectErrors(source, false,false,false, options, "return value for function 's' has no type declaration.\nparameter 'p' for function 's' has no type declaration.\nparameter 'p' for function 's' has no type declaration.\n"); + } + + @Test + public void testSkipSetterParameterHasNoTypeDeclarationWarning_withInferTypesEnabledAndOverrideInferredType() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s() { return \"hello\"; }", + "public function set s(p):void {}", + "}", + "class D extends C {", + "override public function set s(p):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } + + @Test + public void testSetterParameterHasNoTypeDeclarationWarning_withInferTypesDisabledAndOverrideInferredType2() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s():String { return null; }", + "public function set s(p):void {}", + "}", + "class D extends C {", + "override public function set s(p):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=false" + }; + + compileAndExpectErrors(source, false,false,false, options, "parameter 'p' for function 's' has no type declaration.\nparameter 'p' for function 's' has no type declaration.\n"); + } + + @Test + public void testSkipSetterParameterHasNoTypeDeclarationWarning_withInferTypesEnabledAndOverrideInferredType2() + { + String[] imports = new String[] + { + }; + String[] declarations = new String[] + { + }; + String[] testCode = new String[] + { + }; + String[] extra = new String[] + { + "class C {", + "public function get s():String { return null; }", + "public function set s(p):void {}", + "}", + "class D extends C {", + "override public function set s(p):void {}", + "}" + }; + String source = getAS(imports, declarations, testCode, extra); + + String[] options = new String[] + { + "-infer-types=true" + }; + compileAndExpectNoErrors(source, false,false,false, options); + } +}
