This is an automated email from the ASF dual-hosted git repository.
matthiasblaesing pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 5f33fe47b1 Fix handling of classes declared in anonymous functions
new 4e6930488c Merge pull request #6249 from matthiasblaesing/js_fixes
5f33fe47b1 is described below
commit 5f33fe47b1e127eefaf86dcc2aa96d3a2b644096
Author: Matthias Bläsing <[email protected]>
AuthorDate: Sun Jul 23 20:46:52 2023 +0200
Fix handling of classes declared in anonymous functions
Consider this construct:
(function () {
class Directory {
constructor(displayName) {
this.displayName = displayName;
}
}
class ImageFile{
constructor(displayName){
this.displayName = displayName;
}
}
})();
Before this change this construct was not correctly parsed and the
constructor was not cleanly recognised, leading to missing usage
marks (i.e. the displayName above was marked as unused).
A different fallout of that problem is visible with this:
(function () {
class Directory {
constructor(displayName) {
this.displayName = displayName;
}
}
class ImageFile{
constructor(displayName){
this.displayName = displayName;
}
}
function getJsonData(folderURL) {
const body = folderURL;
}
})();
Problems:
- constructor from ImageFile is shown as a toplevel function
- ImageFile is listed without its constructor and without its
displayName property
---
.../structure/classInAnonymousFunction.js | 33 +++++++++++++++++++
.../classInAnonymousFunction.js.structure | 6 ++++
.../structure/classInAnonymousFunction2.js | 36 +++++++++++++++++++++
.../classInAnonymousFunction2.js.structure | 7 ++++
.../javascript2/editor/JsStructureScannerTest.java | 8 +++++
.../modules/javascript2/model/ModelVisitor.java | 21 +++++++++---
.../testfiles/model/classInAnonymousFunction.js | 33 +++++++++++++++++++
.../model/classInAnonymousFunction.js.model | 29 +++++++++++++++++
.../testfiles/model/classInAnonymousFunction2.js | 36 +++++++++++++++++++++
.../model/classInAnonymousFunction2.js.model | 37 ++++++++++++++++++++++
.../modules/javascript2/model/ModelTest.java | 8 +++++
11 files changed, 249 insertions(+), 5 deletions(-)
diff --git
a/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction.js
b/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction.js
new file mode 100644
index 0000000000..2d05a5fb09
--- /dev/null
+++
b/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction.js
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+(function () {
+
+ class Directory {
+ constructor(displayName) {
+ this.displayName = displayName;
+ }
+ }
+
+ class ImageFile{
+ constructor(displayName){
+ this.displayName = displayName;
+ }
+ }
+})();
diff --git
a/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction.js.structure
b/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction.js.structure
new file mode 100644
index 0000000000..0ab97d2a39
--- /dev/null
+++
b/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction.js.structure
@@ -0,0 +1,6 @@
+Directory:CLASS:[PUBLIC]:ESCAPED{Directory}:
+
constructor:CONSTRUCTOR:[PUBLIC]:ESCAPED{constructor}ESCAPED{(}ESCAPED{displayName}ESCAPED{)}<font
color="#999999">ESCAPED{ : }Directory</font>:
+ displayName:FIELD:[PUBLIC]:ESCAPED{displayName}:
+ImageFile:CLASS:[PUBLIC]:ESCAPED{ImageFile}:
+
constructor:CONSTRUCTOR:[PUBLIC]:ESCAPED{constructor}ESCAPED{(}ESCAPED{displayName}ESCAPED{)}<font
color="#999999">ESCAPED{ : }ImageFile</font>:
+ displayName:FIELD:[PUBLIC]:ESCAPED{displayName}:
diff --git
a/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction2.js
b/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction2.js
new file mode 100644
index 0000000000..64c9739527
--- /dev/null
+++
b/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction2.js
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+(function () {
+ class Directory {
+ constructor(displayName) {
+ this.displayName = displayName;
+ }
+ }
+
+ class ImageFile{
+ constructor(displayName){
+ this.displayName = displayName;
+ }
+ }
+
+ function getJsonData(folderURL) {
+ const body = folderURL;
+ }
+})();
\ No newline at end of file
diff --git
a/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction2.js.structure
b/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction2.js.structure
new file mode 100644
index 0000000000..a9b0cc53e3
--- /dev/null
+++
b/webcommon/javascript2.editor/test/unit/data/testfiles/structure/classInAnonymousFunction2.js.structure
@@ -0,0 +1,7 @@
+Directory:CLASS:[PUBLIC]:ESCAPED{Directory}:
+
constructor:CONSTRUCTOR:[PUBLIC]:ESCAPED{constructor}ESCAPED{(}ESCAPED{displayName}ESCAPED{)}<font
color="#999999">ESCAPED{ : }Directory</font>:
+ displayName:FIELD:[PUBLIC]:ESCAPED{displayName}:
+ImageFile:CLASS:[PUBLIC]:ESCAPED{ImageFile}:
+
constructor:CONSTRUCTOR:[PUBLIC]:ESCAPED{constructor}ESCAPED{(}ESCAPED{displayName}ESCAPED{)}<font
color="#999999">ESCAPED{ : }ImageFile</font>:
+ displayName:FIELD:[PUBLIC]:ESCAPED{displayName}:
+getJsonData:METHOD:[PRIVATE]:ESCAPED{getJsonData}ESCAPED{(}ESCAPED{folderURL}ESCAPED{)}<font
color="#999999">ESCAPED{ : }undefined</font>:
diff --git
a/webcommon/javascript2.editor/test/unit/src/org/netbeans/modules/javascript2/editor/JsStructureScannerTest.java
b/webcommon/javascript2.editor/test/unit/src/org/netbeans/modules/javascript2/editor/JsStructureScannerTest.java
index 5073a1edc3..205f858ef5 100644
---
a/webcommon/javascript2.editor/test/unit/src/org/netbeans/modules/javascript2/editor/JsStructureScannerTest.java
+++
b/webcommon/javascript2.editor/test/unit/src/org/netbeans/modules/javascript2/editor/JsStructureScannerTest.java
@@ -766,4 +766,12 @@ public class JsStructureScannerTest extends JsTestBase {
public void testObjectNameMatchingNestedFunction() throws Exception {
checkStructure("testfiles/structure/objectNameMatchingNestedFunction.js");
}
+
+ public void testClassInAnonymousFunction() throws Exception {
+ checkStructure("testfiles/structure/classInAnonymousFunction.js");
+ }
+
+ public void testClassInAnonymousFunction2() throws Exception {
+ checkStructure("testfiles/structure/classInAnonymousFunction2.js");
+ }
}
diff --git
a/webcommon/javascript2.model/src/org/netbeans/modules/javascript2/model/ModelVisitor.java
b/webcommon/javascript2.model/src/org/netbeans/modules/javascript2/model/ModelVisitor.java
index aeeb25e8df..752c09985e 100644
---
a/webcommon/javascript2.model/src/org/netbeans/modules/javascript2/model/ModelVisitor.java
+++
b/webcommon/javascript2.model/src/org/netbeans/modules/javascript2/model/ModelVisitor.java
@@ -62,10 +62,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Level;
@@ -2479,6 +2479,12 @@ public class ModelVisitor extends PathNodeVisitor
implements ModelResolver {
return super.enterUnaryNode(unaryNode);
}
+ // Track objects pushed to ModelBuilder from VarNode handling. objects are
+ // only conditionally pushed enterVarNode and thus leaveVarNode must only
+ // pop that state if it came from enterVarNode. There should be a better
+ // solution, but should be ok in the interim
+ private final Map<JsObject, VarNode> varNodeScopes = new
IdentityHashMap<>();
+
@Override
public boolean enterVarNode(VarNode varNode) {
Node init = varNode.getInit();
@@ -2522,10 +2528,10 @@ public class ModelVisitor extends PathNodeVisitor
implements ModelResolver {
// }
}
- if (!(init instanceof ObjectNode || rNode != null
- || init instanceof LiteralNode.ArrayLiteralNode
- || init instanceof ClassNode
- || varNode.isExport())) {
+ if (!(init instanceof ObjectNode || rNode != null
+ || init instanceof LiteralNode.ArrayLiteralNode
+ || init instanceof ClassNode
+ || varNode.isExport())) {
JsObject parent = modelBuilder.getCurrentObject();
//parent = canBeSingletonPattern(1) ? resolveThis(parent) : parent;
if (parent instanceof CatchBlockImpl) {
@@ -2605,6 +2611,7 @@ public class ModelVisitor extends PathNodeVisitor
implements ModelResolver {
}
}
+ varNodeScopes.put(variable, varNode);
modelBuilder.setCurrentObject(variable);
Collection<TypeUsage> types =
ModelUtils.resolveSemiTypeOfExpression(modelBuilder, init);
if (modelBuilder.getCurrentWith() != null) {
@@ -2636,6 +2643,7 @@ public class ModelVisitor extends PathNodeVisitor
implements ModelResolver {
}
if (variable != null) {
variable.setJsKind(JsElement.Kind.OBJECT_LITERAL);
+ varNodeScopes.put(variable, varNode);
modelBuilder.setCurrentObject(variable);
}
}
@@ -2747,6 +2755,9 @@ public class ModelVisitor extends PathNodeVisitor
implements ModelResolver {
}
+ }
+ if (varNodeScopes.containsKey(modelBuilder.getCurrentObject())) {
+ varNodeScopes.remove(modelBuilder.getCurrentObject());
modelBuilder.reset();
}
return super.leaveVarNode(varNode);
diff --git
a/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction.js
b/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction.js
new file mode 100644
index 0000000000..2d05a5fb09
--- /dev/null
+++
b/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction.js
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+(function () {
+
+ class Directory {
+ constructor(displayName) {
+ this.displayName = displayName;
+ }
+ }
+
+ class ImageFile{
+ constructor(displayName){
+ this.displayName = displayName;
+ }
+ }
+})();
diff --git
a/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction.js.model
b/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction.js.model
new file mode 100644
index 0000000000..03610cc59f
--- /dev/null
+++
b/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction.js.model
@@ -0,0 +1,29 @@
+FUNCTION classInAnonymousFunction [ANONYMOUS: false, DECLARED: true -
classInAnonymousFunction, MODIFIERS: PUBLIC, FILE]
+# RETURN TYPES
+undefined, RESOLVED: true
+# PROPERTIES
+classInAnonymousFunctionL#20 : FUNCTION classInAnonymousFunctionL#20
[ANONYMOUS: true, DECLARED: true - classInAnonymousFunctionL#20, MODIFIERS:
PUBLIC, FUNCTION]
+ # RETURN TYPES
+ undefined, RESOLVED: true
+ # PROPERTIES
+ Directory : OBJECT Directory [ANONYMOUS: false,
DECLARED: true - Directory, MODIFIERS: PUBLIC, CLASS]
+ # PROPERTIES
+ constructor : FUNCTION constructor
[ANONYMOUS: false, DECLARED: true - constructor, MODIFIERS: PUBLIC, CONSTRUCTOR]
+ # RETURN TYPES
+
classInAnonymousFunctionL#20.Directory, RESOLVED: true
+ # PARAMETERS
+ OBJECT displayName
[ANONYMOUS: false, DECLARED: true - displayName, MODIFIERS: PUBLIC, PARAMETER]
+ # PROPERTIES
+ arguments : OBJECT
arguments [ANONYMOUS: false, DECLARED: false - arguments, MODIFIERS: PRIVATE,
VARIABLE]
+ displayName : OBJECT displayName
[ANONYMOUS: false, DECLARED: true - displayName, MODIFIERS: PUBLIC, PROPERTY]
+ ImageFile : OBJECT ImageFile [ANONYMOUS: false,
DECLARED: true - ImageFile, MODIFIERS: PUBLIC, CLASS]
+ # PROPERTIES
+ constructor : FUNCTION constructor
[ANONYMOUS: false, DECLARED: true - constructor, MODIFIERS: PUBLIC, CONSTRUCTOR]
+ # RETURN TYPES
+
classInAnonymousFunctionL#20.ImageFile, RESOLVED: true
+ # PARAMETERS
+ OBJECT displayName
[ANONYMOUS: false, DECLARED: true - displayName, MODIFIERS: PUBLIC, PARAMETER]
+ # PROPERTIES
+ arguments : OBJECT
arguments [ANONYMOUS: false, DECLARED: false - arguments, MODIFIERS: PRIVATE,
VARIABLE]
+ displayName : OBJECT displayName
[ANONYMOUS: false, DECLARED: true - displayName, MODIFIERS: PUBLIC, PROPERTY]
+ arguments : OBJECT arguments [ANONYMOUS: false,
DECLARED: false - arguments, MODIFIERS: PRIVATE, VARIABLE]
diff --git
a/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction2.js
b/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction2.js
new file mode 100644
index 0000000000..64c9739527
--- /dev/null
+++
b/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction2.js
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+(function () {
+ class Directory {
+ constructor(displayName) {
+ this.displayName = displayName;
+ }
+ }
+
+ class ImageFile{
+ constructor(displayName){
+ this.displayName = displayName;
+ }
+ }
+
+ function getJsonData(folderURL) {
+ const body = folderURL;
+ }
+})();
\ No newline at end of file
diff --git
a/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction2.js.model
b/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction2.js.model
new file mode 100644
index 0000000000..487e4d50d2
--- /dev/null
+++
b/webcommon/javascript2.model/test/unit/data/testfiles/model/classInAnonymousFunction2.js.model
@@ -0,0 +1,37 @@
+FUNCTION classInAnonymousFunction2 [ANONYMOUS: false, DECLARED: true -
classInAnonymousFunction2, MODIFIERS: PUBLIC, FILE]
+# RETURN TYPES
+undefined, RESOLVED: true
+# PROPERTIES
+classInAnonymousFunction2L#20 : FUNCTION classInAnonymousFunction2L#20
[ANONYMOUS: true, DECLARED: true - classInAnonymousFunction2L#20, MODIFIERS:
PUBLIC, FUNCTION]
+ # RETURN TYPES
+ undefined, RESOLVED: true
+ # PROPERTIES
+ Directory : OBJECT Directory [ANONYMOUS:
false, DECLARED: true - Directory, MODIFIERS: PUBLIC, CLASS]
+ # PROPERTIES
+ constructor : FUNCTION constructor
[ANONYMOUS: false, DECLARED: true - constructor, MODIFIERS: PUBLIC, CONSTRUCTOR]
+ # RETURN TYPES
+
classInAnonymousFunction2L#20.Directory, RESOLVED: true
+ # PARAMETERS
+ OBJECT displayName
[ANONYMOUS: false, DECLARED: true - displayName, MODIFIERS: PUBLIC, PARAMETER]
+ # PROPERTIES
+ arguments : OBJECT
arguments [ANONYMOUS: false, DECLARED: false - arguments, MODIFIERS: PRIVATE,
VARIABLE]
+ displayName : OBJECT displayName
[ANONYMOUS: false, DECLARED: true - displayName, MODIFIERS: PUBLIC, PROPERTY]
+ ImageFile : OBJECT ImageFile [ANONYMOUS:
false, DECLARED: true - ImageFile, MODIFIERS: PUBLIC, CLASS]
+ # PROPERTIES
+ constructor : FUNCTION constructor
[ANONYMOUS: false, DECLARED: true - constructor, MODIFIERS: PUBLIC, CONSTRUCTOR]
+ # RETURN TYPES
+
classInAnonymousFunction2L#20.ImageFile, RESOLVED: true
+ # PARAMETERS
+ OBJECT displayName
[ANONYMOUS: false, DECLARED: true - displayName, MODIFIERS: PUBLIC, PARAMETER]
+ # PROPERTIES
+ arguments : OBJECT
arguments [ANONYMOUS: false, DECLARED: false - arguments, MODIFIERS: PRIVATE,
VARIABLE]
+ displayName : OBJECT displayName
[ANONYMOUS: false, DECLARED: true - displayName, MODIFIERS: PUBLIC, PROPERTY]
+ arguments : OBJECT arguments [ANONYMOUS:
false, DECLARED: false - arguments, MODIFIERS: PRIVATE, VARIABLE]
+ getJsonData : FUNCTION getJsonData [ANONYMOUS:
false, DECLARED: true - getJsonData, MODIFIERS: PRIVATE, METHOD]
+ # RETURN TYPES
+ undefined, RESOLVED: true
+ # PARAMETERS
+ OBJECT folderURL [ANONYMOUS: false,
DECLARED: true - folderURL, MODIFIERS: PUBLIC, PARAMETER]
+ # PROPERTIES
+ arguments : OBJECT arguments
[ANONYMOUS: false, DECLARED: false - arguments, MODIFIERS: PRIVATE, VARIABLE]
+ body : OBJECT body [ANONYMOUS:
false, DECLARED: true - body, MODIFIERS: PRIVATE, CONSTANT]
diff --git
a/webcommon/javascript2.model/test/unit/src/org/netbeans/modules/javascript2/model/ModelTest.java
b/webcommon/javascript2.model/test/unit/src/org/netbeans/modules/javascript2/model/ModelTest.java
index e7d630599b..8042180285 100644
---
a/webcommon/javascript2.model/test/unit/src/org/netbeans/modules/javascript2/model/ModelTest.java
+++
b/webcommon/javascript2.model/test/unit/src/org/netbeans/modules/javascript2/model/ModelTest.java
@@ -298,4 +298,12 @@ public class ModelTest extends ModelTestBase {
public void testClassConstructor() throws Exception {
checkModel("testfiles/model/classConstructor.js");
}
+
+ public void testClassInAnonymousFunction() throws Exception {
+ checkModel("testfiles/model/classInAnonymousFunction.js");
+ }
+
+ public void testClassInAnonymousFunction2() throws Exception {
+ checkModel("testfiles/model/classInAnonymousFunction2.js");
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists