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 741fa0139aa0c70d880fb4e0ecad90d9bbd72360 Author: Josh Tynjala <[email protected]> AuthorDate: Mon Sep 26 13:28:51 2022 -0700 linter: duplicate-object-keys --- .../main/java/org/apache/royale/linter/LINTER.java | 4 + .../apache/royale/linter/config/Configuration.java | 16 ++++ .../linter/rules/DuplicateObjectKeysRule.java | 104 +++++++++++++++++++++ 3 files changed, 124 insertions(+) diff --git a/linter/src/main/java/org/apache/royale/linter/LINTER.java b/linter/src/main/java/org/apache/royale/linter/LINTER.java index ba4de97ca..03d6c0549 100644 --- a/linter/src/main/java/org/apache/royale/linter/LINTER.java +++ b/linter/src/main/java/org/apache/royale/linter/LINTER.java @@ -54,6 +54,7 @@ import org.apache.royale.linter.rules.ClassNameRule; import org.apache.royale.linter.rules.ConstantNameRule; import org.apache.royale.linter.rules.ConstructorDispatchEventRule; import org.apache.royale.linter.rules.ConstructorReturnTypeRule; +import org.apache.royale.linter.rules.DuplicateObjectKeysRule; import org.apache.royale.linter.rules.DynamicClassRule; import org.apache.royale.linter.rules.EmptyCommentRule; import org.apache.royale.linter.rules.EmptyFunctionBodyRule; @@ -262,6 +263,9 @@ public class LINTER { if (configuration.getConstructorDispatchEvent()) { rules.add(new ConstructorDispatchEventRule()); } + if (configuration.getDuplicateObjectKeys()) { + rules.add(new DuplicateObjectKeysRule()); + } if (configuration.getDynamicClass()) { rules.add(new DynamicClassRule()); } diff --git a/linter/src/main/java/org/apache/royale/linter/config/Configuration.java b/linter/src/main/java/org/apache/royale/linter/config/Configuration.java index 5d1975128..7aac037d7 100644 --- a/linter/src/main/java/org/apache/royale/linter/config/Configuration.java +++ b/linter/src/main/java/org/apache/royale/linter/config/Configuration.java @@ -279,6 +279,22 @@ public class Configuration { this.constantName = b; } + // + // 'duplicate-object-keys' option + // + + private boolean duplicateObjectKeys = false; + + public boolean getDuplicateObjectKeys() { + return duplicateObjectKeys; + } + + @Config + @Mapping("duplicate-object-keys") + public void setDuplicateObjectKeys(ConfigurationValue cv, boolean b) { + this.duplicateObjectKeys = b; + } + // // 'constructor-dispatch-event' option // diff --git a/linter/src/main/java/org/apache/royale/linter/rules/DuplicateObjectKeysRule.java b/linter/src/main/java/org/apache/royale/linter/rules/DuplicateObjectKeysRule.java new file mode 100644 index 000000000..d1fef4dd7 --- /dev/null +++ b/linter/src/main/java/org/apache/royale/linter/rules/DuplicateObjectKeysRule.java @@ -0,0 +1,104 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.linter.rules; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.royale.compiler.internal.tree.as.ContainerNode; +import org.apache.royale.compiler.problems.CompilerProblem; +import org.apache.royale.compiler.problems.ICompilerProblem; +import org.apache.royale.compiler.tree.ASTNodeID; +import org.apache.royale.compiler.tree.as.IASNode; +import org.apache.royale.compiler.tree.as.IExpressionNode; +import org.apache.royale.compiler.tree.as.IIdentifierNode; +import org.apache.royale.compiler.tree.as.ILiteralContainerNode; +import org.apache.royale.compiler.tree.as.ILiteralNode; +import org.apache.royale.compiler.tree.as.ILiteralNode.LiteralType; +import org.apache.royale.compiler.tree.as.IObjectLiteralValuePairNode; +import org.apache.royale.linter.LinterRule; +import org.apache.royale.linter.NodeVisitor; +import org.apache.royale.linter.TokenQuery; + +/** + * Check that each key in an object literal is unique. + */ +public class DuplicateObjectKeysRule extends LinterRule { + @Override + public Map<ASTNodeID, NodeVisitor> getNodeVisitors() { + Map<ASTNodeID, NodeVisitor> result = new HashMap<>(); + result.put(ASTNodeID.ObjectLiteralExpressionID, (node, tokenQuery, problems) -> { + checkLiteralContainerNode((ILiteralContainerNode) node, tokenQuery, problems); + }); + return result; + } + + private void checkLiteralContainerNode(ILiteralContainerNode objectLiteralNode, TokenQuery tokenQuery, Collection<ICompilerProblem> problems) { + if (!LiteralType.OBJECT.equals(objectLiteralNode.getLiteralType())) { + return; + } + ContainerNode contentsNode = objectLiteralNode.getContentsNode(); + if (contentsNode == null) { + return; + } + Set<String> keyNames = new HashSet<>(); + for (int i = 0; i < contentsNode.getChildCount(); i++) { + IASNode child = contentsNode.getChild(i); + if (!(child instanceof IObjectLiteralValuePairNode)) { + continue; + } + String keyName = null; + IObjectLiteralValuePairNode valuePairNode = (IObjectLiteralValuePairNode) child; + IExpressionNode nameNode = valuePairNode.getNameNode(); + if (nameNode instanceof IIdentifierNode) { + IIdentifierNode identifierNode = (IIdentifierNode) nameNode; + keyName = identifierNode.getName(); + } else if (nameNode instanceof ILiteralNode) { + ILiteralNode literalNode = (ILiteralNode) nameNode; + if (!LiteralType.STRING.equals(literalNode.getLiteralType())) { + continue; + } + keyName = literalNode.getValue(); + } + if (keyName != null) { + if (keyNames.contains(keyName)) { + problems.add(new DuplicateObjectKeysLinterProblem(nameNode, keyName)); + } else { + keyNames.add(keyName); + } + } + } + } + + public static class DuplicateObjectKeysLinterProblem extends CompilerProblem { + public static final String DESCRIPTION = "Object literal contains duplicate key '${keyName}'"; + + public DuplicateObjectKeysLinterProblem(IExpressionNode node, String keyName) + { + super(node); + this.keyName = keyName; + } + + public String keyName; + } +}
