diff --git a/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/ExtendedText.java b/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/ExtendedText.java index 2833d8cce6..8ac81791c0 100644 --- a/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/ExtendedText.java +++ b/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/ExtendedText.java @@ -78,9 +78,8 @@ public Location mapLocation(Range range) { return new Location(uri, range); } for (Mapper mapper : mappers) { - Location result = mapper.apply(startCharacter, endCharacter); - if (result != null) { - return result; + if (mapper.canApply(startCharacter, endCharacter)) { + return mapper.apply(startCharacter, endCharacter); } } throw new RuntimeException("Cannot find original position"); diff --git a/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/InsertMapper.java b/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/InsertMapper.java index 27c2de0848..729bf4e946 100644 --- a/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/InsertMapper.java +++ b/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/InsertMapper.java @@ -23,16 +23,24 @@ * Handle insert mapping situations */ class InsertMapper implements Mapper { + @Override + public boolean canApply(MappedCharacter startCharacter, MappedCharacter endCharacter) { + if (startCharacter == null || endCharacter == null) { + return false; + } + if (startCharacter.getOriginalPosition() == null) { + return false; + } + return !startCharacter.getUri().equals(endCharacter.getUri()); + } + @Override public Location apply(MappedCharacter startCharacter, MappedCharacter endCharacter) { - if (startCharacter != null && !startCharacter.getUri().equals(endCharacter.getUri()) && startCharacter.getOriginalPosition() != null) { Map ilm = endCharacter.getInitialLocationMap(); Location location = ilm != null ? ilm.get(startCharacter.getUri()) : null; if (location != null) { return new Location(startCharacter.getUri(), new Range(startCharacter.getOriginalPosition(), location.getRange().getEnd())); } return new Location(startCharacter.getUri(), new Range(startCharacter.getOriginalPosition(), startCharacter.getOriginalPosition())); - } - return null; } } diff --git a/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/Mapper.java b/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/Mapper.java index 04272e77e3..8afee9b908 100644 --- a/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/Mapper.java +++ b/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/Mapper.java @@ -20,5 +20,6 @@ * Provides mapping functionality */ interface Mapper { + boolean canApply(MappedCharacter startCharacter, MappedCharacter endCharacter); Location apply(MappedCharacter startCharacter, MappedCharacter endCharacter); } diff --git a/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/ReplaceMapper.java b/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/ReplaceMapper.java index ca4bd53d38..e4f7ffa80f 100644 --- a/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/ReplaceMapper.java +++ b/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/ReplaceMapper.java @@ -24,25 +24,29 @@ */ class ReplaceMapper implements Mapper { - @Override - public Location apply(MappedCharacter startCharacter, MappedCharacter endCharacter) { - if (!startCharacter.getUri().equals(endCharacter.getUri())) { - endCharacter = startCharacter; + @Override + public boolean canApply(MappedCharacter startCharacter, MappedCharacter endCharacter) { + if (!startCharacter.getUri().equals(endCharacter.getUri())) { + endCharacter = startCharacter; + } + return startCharacter.getOriginalPosition() == null || endCharacter.getOriginalPosition() == null; } - if (startCharacter.getOriginalPosition() == null || endCharacter.getOriginalPosition() == null) { - if (startCharacter.getInstantLocation() == null && endCharacter.getInstantLocation() == null) { - throw new RuntimeException("Cannot find original position"); - } - if (endCharacter.getOriginalPosition() != null) { - Range calculatedRange = new Range(startCharacter.getInstantLocation().getRange().getStart(), endCharacter.getOriginalPosition()); - return new Location(startCharacter.getUri(), calculatedRange); - } else if (startCharacter.getOriginalPosition() != null) { - Range calculatedRange = new Range(startCharacter.getOriginalPosition(), endCharacter.getInstantLocation().getRange().getEnd()); - return new Location(startCharacter.getUri(), calculatedRange); - } - return Optional.ofNullable(startCharacter.getInstantLocation()).orElse(endCharacter.getInstantLocation()); + @Override + public Location apply(MappedCharacter startCharacter, MappedCharacter endCharacter) { + if (!startCharacter.getUri().equals(endCharacter.getUri())) { + endCharacter = startCharacter; + } + if (startCharacter.getInstantLocation() == null && endCharacter.getInstantLocation() == null) { + throw new RuntimeException("Cannot find original position"); + } + if (endCharacter.getOriginalPosition() != null) { + Range calculatedRange = new Range(startCharacter.getInstantLocation().getRange().getStart(), endCharacter.getOriginalPosition()); + return new Location(startCharacter.getUri(), calculatedRange); + } else if (startCharacter.getOriginalPosition() != null) { + Range calculatedRange = new Range(startCharacter.getOriginalPosition(), endCharacter.getInstantLocation().getRange().getEnd()); + return new Location(startCharacter.getUri(), calculatedRange); + } + return Optional.ofNullable(startCharacter.getInstantLocation()).orElse(endCharacter.getInstantLocation()); } - return null; - } } diff --git a/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/StraightforwardMapper.java b/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/StraightforwardMapper.java index cb4823a07b..b8cba41142 100644 --- a/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/StraightforwardMapper.java +++ b/server/common/src/main/java/org/eclipse/lsp/cobol/common/mapping/StraightforwardMapper.java @@ -23,6 +23,11 @@ */ class StraightforwardMapper implements Mapper { + @Override + public boolean canApply(MappedCharacter startCharacter, MappedCharacter endCharacter) { + return true; + } + @Override public Location apply(MappedCharacter startCharacter, MappedCharacter endCharacter) { Position startPosition = new Position(startCharacter.getOriginalPosition().getLine(), startCharacter.getOriginalPosition().getCharacter()); diff --git a/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/Node.java b/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/Node.java index fbdb2f9f68..917875d25b 100644 --- a/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/Node.java +++ b/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/Node.java @@ -14,15 +14,13 @@ */ package org.eclipse.lsp.cobol.common.model.tree; -import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.ToString; import org.eclipse.lsp.cobol.common.model.Locality; import org.eclipse.lsp.cobol.common.model.NodeType; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; import java.util.stream.Stream; @@ -30,15 +28,14 @@ /** The class represents a Node in source structure tree. */ @ToString @Getter -@EqualsAndHashCode public abstract class Node { @Setter protected Locality locality; @Setter protected String text; private final NodeType nodeType; private final String dialect; - @EqualsAndHashCode.Exclude private final List children = new CopyOnWriteArrayList<>(); - @EqualsAndHashCode.Exclude @ToString.Exclude @Setter private transient Node parent; + private final List children = new CopyOnWriteArrayList<>(); + @ToString.Exclude @Setter private transient Node parent; protected Node(Locality location, NodeType nodeType, String dialect) { this.locality = location; @@ -103,6 +100,58 @@ public Stream getDepthFirstStream() { return Stream.concat(Stream.of(this), children.stream().flatMap(Node::getDepthFirstStream)); } + /** + * Get a list with all nested children starting with this instance. + * + * @return a list with all underline children. + */ + public List getDepthFirstList(Predicate nodePredicate) { + ArrayList result = new ArrayList<>(); + if (nodePredicate.test(this)) { + result.add(this); + } + LinkedList queue = new LinkedList<>(getChildren()); + while (!queue.isEmpty()) { + Node node = queue.remove(); + if (nodePredicate.test(node)) { + result.add(node); + } + for (Node child : node.getChildren()) { + queue.addFirst(child); + } + } + return result; + } + + /** + * Get the first node that meet predicate condition. + * + * @return a node if there is any or null. + */ + public Node getDepthFirstFirstNode(Predicate nodePredicate) { + if (nodePredicate.test(this)) { + return this; + } + LinkedList queue = new LinkedList<>(); + for (Node child: getChildren()) { + if (nodePredicate.test(child)) { + return child; + } + queue.add(child); + } + + while (!queue.isEmpty()) { + Node node = queue.remove(); + for (Node child : node.getChildren()) { + if (nodePredicate.test(child)) { + return child; + } + queue.addFirst(child); + } + } + return null; + } + /** * Get nearest parent with specified type. * @@ -110,8 +159,11 @@ public Stream getDepthFirstStream() { * @return an optional with requested nearest node. */ public Optional getNearestParentByType(NodeType type) { - return Optional.ofNullable(parent) - .flatMap(it -> (it.nodeType == type) ? Optional.of(it) : it.getNearestParentByType(type)); + Node result = parent; + while (result != null && !type.equals(result.getNodeType())) { + result = result.getParent(); + } + return Optional.ofNullable(result); } /** * Get nearest parent using predicate. @@ -132,4 +184,22 @@ public Optional getNearestParent(Predicate predicate) { public Optional getProgram() { return getNearestParentByType(NodeType.PROGRAM).map(ProgramNode.class::cast); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Node node = (Node) o; + return Objects.equals(locality, node.locality) && Objects.equals(text, node.text) && nodeType == node.nodeType && Objects.equals(dialect, node.dialect); + } + + @Override + public int hashCode() { + int result = Objects.hashCode(locality); + result = 31 * result + Objects.hashCode(text); + result = 31 * result + Objects.hashCode(nodeType); + result = 31 * result + Objects.hashCode(dialect); + return result; + } } diff --git a/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableDefinitionNode.java b/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableDefinitionNode.java index f96847e9dc..80a0ae43e0 100644 --- a/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableDefinitionNode.java +++ b/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableDefinitionNode.java @@ -38,7 +38,7 @@ @Getter @ToString(callSuper = true) @SuppressWarnings("squid:S107") -@EqualsAndHashCode(callSuper = true) +//@EqualsAndHashCode(callSuper = true) public final class VariableDefinitionNode extends Node { private static final String SPACES_AFTER_NEWLINE_REGEX = "[\\r\\n|\\r|\\n]\\s+"; private final int level; diff --git a/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableNode.java b/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableNode.java index 141647c9f5..4e9bb22d5e 100644 --- a/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableNode.java +++ b/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableNode.java @@ -31,6 +31,7 @@ import org.eclipse.lsp4j.Range; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import static java.util.stream.Collectors.toList; @@ -39,16 +40,14 @@ import static org.eclipse.lsp.cobol.common.model.NodeType.VARIABLE_DEFINITION_NAME; /** The abstract class for all variable definitions. */ -@Getter @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) public abstract class VariableNode extends Node implements DefinedAndUsedStructure { - public static final String PREFIX = " "; - private final VariableType variableType; - private final String name; - @Setter private boolean global; - @EqualsAndHashCode.Exclude private final List usages = new ArrayList<>(); + @Getter private final VariableType variableType; + @Getter private final String name; + @Getter @Setter private boolean global; + @EqualsAndHashCode.Exclude private final LinkedHashSet usages = new LinkedHashSet<>(); protected VariableNode( Locality location, String name, VariableType variableType, boolean global) { @@ -189,6 +188,11 @@ private String getDisplayStringWithConditionals() { return String.join("\n", result); } + @Override + public List getUsages() { + return new ArrayList<>(usages); + } + private static String prepend(String prefix, String text) { return prefix + text.replace("\n", "\n" + prefix); } diff --git a/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableUsageNode.java b/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableUsageNode.java index 8558a56b50..4af96a1cc3 100644 --- a/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableUsageNode.java +++ b/server/common/src/main/java/org/eclipse/lsp/cobol/common/model/tree/variable/VariableUsageNode.java @@ -60,7 +60,11 @@ public VariableUsageNode( @Override public List getDefinitions() { - return definitions.stream().map(VariableNode::getLocality).map(Locality::toLocation).collect(Collectors.toList()); + List result = new ArrayList<>(definitions.size()); + for (VariableNode definition : definitions) { + result.add(definition.getLocality().toLocation()); + } + return result; } /** diff --git a/server/common/src/main/java/org/eclipse/lsp/cobol/common/symbols/SymbolTable.java b/server/common/src/main/java/org/eclipse/lsp/cobol/common/symbols/SymbolTable.java index 183c07e790..935337fb4a 100644 --- a/server/common/src/main/java/org/eclipse/lsp/cobol/common/symbols/SymbolTable.java +++ b/server/common/src/main/java/org/eclipse/lsp/cobol/common/symbols/SymbolTable.java @@ -20,6 +20,8 @@ import org.eclipse.lsp.cobol.common.model.tree.CodeBlockDefinitionNode; import org.eclipse.lsp.cobol.common.model.tree.ProgramNode; import org.eclipse.lsp.cobol.common.model.tree.variable.VariableNode; +import org.eclipse.lsp4j.Range; +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; import java.util.ArrayList; import java.util.HashMap; @@ -40,6 +42,12 @@ public class SymbolTable { * @return string value of a generated key */ public static String generateKey(ProgramNode program) { - return program.getProgramName() + "%" + program.getLocality().getUri() + "%" + program.getLocality().getRange(); + Range range = program.getLocality().getRange(); + String rangeString = "[" + + range.getStart().getLine() + ", " + range.getStart().getCharacter() + + "-" + + range.getEnd().getLine() + ", " + range.getEnd().getCharacter() + + "]"; + return program.getProgramName() + "%" + program.getLocality().getUri() + "%" + rangeString; } } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processor/AstProcessor.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processor/AstProcessor.java index 186a662404..fc97537040 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processor/AstProcessor.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processor/AstProcessor.java @@ -34,14 +34,13 @@ */ @Slf4j public class AstProcessor { - /** * The entry point to AST processing * - * @param analysisConfig - * @param ctx processing context - * @param analysisContext - * @param rootNode the root node of AST + * @param analysisConfig analysis config + * @param ctx processing context + * @param analysisContext analysis context + * @param rootNode the root node of AST * @return a list of errors */ public List processSyntaxTree(AnalysisConfig analysisConfig, ProcessingContext ctx, AnalysisContext analysisContext, Node rootNode) { @@ -58,7 +57,7 @@ public List processSyntaxTree(AnalysisConfig analysisConfig, Proces return ctx.getErrors(); } - /** + /** * Process tree node and its children after tree construction. * * @param phase processing phase @@ -74,7 +73,7 @@ public void process(ProcessingPhase phase, Node node, ProcessingContext ctx) { /** * Process tree node and its children after tree construction. * - * @param processor list of available processors + * @param processors list of available processors * @param node a node to process * @param ctx processing context */ @@ -82,12 +81,16 @@ private void process(Map, List>> Node node, ProcessingContext ctx) { ThreadInterruptionUtil.checkThreadInterrupted(); final Class nodeClass = node.getClass(); - processors.forEach((key, value) -> { - if (!key.isAssignableFrom(nodeClass)) - return; - for (Processor p : value) - ((Processor) p).accept(node, ctx); - }); - node.getChildren().forEach(n -> process(processors, n, ctx)); + for (Map.Entry, List>> entry : processors.entrySet()) { + Class key = entry.getKey(); + List> value = entry.getValue(); + if (!key.isAssignableFrom(nodeClass)) + continue; + for (Processor p : value) + ((Processor) p).accept(node, ctx); + } + for (Node n : node.getChildren()) { + process(processors, n, ctx); + } } } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FileEntryProcess.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FileEntryProcess.java index 460b900adf..0bd6ef3582 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FileEntryProcess.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FileEntryProcess.java @@ -23,7 +23,9 @@ import org.eclipse.lsp.cobol.common.model.tree.FileEntryNode; import org.eclipse.lsp.cobol.common.model.tree.variable.VariableDefinitionNode; +import java.util.List; import java.util.Optional; + /** FileEntryNode processor */ public class FileEntryProcess implements Processor { @Override @@ -34,12 +36,18 @@ public void accept(FileEntryNode node, ProcessingContext ctx) { return; } ProgramNode program = programOpt.get(); - program - .getDepthFirstStream() - .filter(Node.hasType(NodeType.VARIABLE_DEFINITION)) - .map(VariableDefinitionNode.class::cast) - .filter(n -> n.getLevel() == VariableConstants.LEVEL_FD_SD) - .filter(n -> n.getVariableName().getName().equals(node.getFileName())) - .forEach(n -> n.setFileControlClause(node.getFileControlClause())); + List collected = program.getDepthFirstList(n -> { + if (n.getNodeType() != NodeType.VARIABLE_DEFINITION) { + return false; + } + if (((VariableDefinitionNode) n).getLevel() != VariableConstants.LEVEL_FD_SD) { + return false; + } + return ((VariableDefinitionNode) n).getVariableName().getName().equals(node.getFileName()); + }); + + for (Node n : collected) { + ((VariableDefinitionNode) n).setFileControlClause(node.getFileControlClause()); + } } } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FileOperationProcess.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FileOperationProcess.java index 271ab940f0..97b04201af 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FileOperationProcess.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FileOperationProcess.java @@ -103,16 +103,25 @@ private void checkFileOpenedBeforeOperation( } private boolean fileIsExternal(FileOperationStatementNode node) { - if (!node.getNearestParentByType(PROGRAM).isPresent()) return false; - Optional fileDescriptionNode = - node.getNearestParentByType(PROGRAM) - .get() - .getDepthFirstStream() - .filter(FileDescriptionNode.class::isInstance) - .map(FileDescriptionNode.class::cast) - .filter(fdNode -> fdNode.getName().equalsIgnoreCase(node.getFilename().getName())) - .findFirst(); - return fileDescriptionNode.map(FileDescriptionNode::isExternal).orElse(false); + if (!node.getNearestParentByType(PROGRAM).isPresent()) { + return false; + } + Node programNode = node.getNearestParentByType(PROGRAM).orElse(null); + if (programNode == null) { + return false; + } + + Node fd = programNode.getDepthFirstFirstNode(n -> { + if (n.getClass() != FileDescriptionNode.class) { + return false; + } + FileDescriptionNode fdNode = (FileDescriptionNode) n; + if (!fdNode.isExternal()) { + return false; + } + return fdNode.getName().equalsIgnoreCase(node.getFilename().getName()); + }); + return fd != null; } private void checkFileOpenedBeforeOperation( @@ -122,29 +131,36 @@ private void checkFileOpenedBeforeOperation( List expectedFileKind, ProcessingContext ctx, String messageTemplate) { - node.getNearestParentByType(NodeType.PROGRAM) - .ifPresent( - pnode -> { - boolean isFileOpened = - pnode - .getDepthFirstStream() - .filter(Node.hasType(OPEN_STATEMENT)) - .map(OpenStatementNode.class::cast) - .anyMatch( - n -> - n.getFilename().getName().equalsIgnoreCase(filename) - && expectedFileKind.contains(n.getFileOperationKind())); - if (!isFileOpened && Objects.nonNull(errorLocality)) { - ctx.getErrors() - .add( - SyntaxError.syntaxError() - .errorSource(ErrorSource.PARSING) - .severity(ErrorSeverity.WARNING) - .location(errorLocality.toOriginalLocation()) - .messageTemplate(MessageTemplate.of(messageTemplate)) - .build()); - } - }); + + Boolean isFileOpened = isFileOpen(node, filename, expectedFileKind); + if (isFileOpened == null) { + return; + } + + if (!isFileOpened && Objects.nonNull(errorLocality)) { + ctx.getErrors().add(SyntaxError.syntaxError().errorSource(ErrorSource.PARSING).severity(ErrorSeverity.WARNING) + .location(errorLocality.toOriginalLocation()).messageTemplate(MessageTemplate.of(messageTemplate)) + .build()); + } + } + + private static Boolean isFileOpen(Node node, String filename, List expectedFileKind) { + Optional nearestParentByType = node.getNearestParentByType(PROGRAM); + if (!nearestParentByType.isPresent()) { + return null; + } + + Node result = nearestParentByType.get().getDepthFirstFirstNode(n -> { + if (n.getNodeType() != OPEN_STATEMENT) { + return false; + } + OpenStatementNode osn = (OpenStatementNode) n; + if (!expectedFileKind.contains(osn.getFileOperationKind())) { + return false; + } + return osn.getFilename().getName().equalsIgnoreCase(filename); + }); + return result != null; } private void checkFileOpenedForWrite( diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FunctionUsageReferenceEnricher.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FunctionUsageReferenceEnricher.java index ca5cfe8b99..4202f271a9 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FunctionUsageReferenceEnricher.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/FunctionUsageReferenceEnricher.java @@ -25,9 +25,9 @@ import org.eclipse.lsp.cobol.common.processor.Processor; import org.eclipse.lsp.cobol.core.engine.symbols.SymbolAccumulatorService; +import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; /** * Enriches the @{@link QualifiedReferenceNode}'s children by replacing the Variable node @@ -41,11 +41,12 @@ public class FunctionUsageReferenceEnricher implements Processor program = node.getProgram(); if (!program.isPresent()) return; - List usageNodes = - node.getChildren().stream() - .filter(Node.hasType(NodeType.VARIABLE_USAGE)) - .map(VariableUsageNode.class::cast) - .collect(Collectors.toList()); + List usageNodes = new ArrayList<>(); + for (Node child : node.getChildren()) { + if (child.getNodeType() == NodeType.VARIABLE_USAGE) { + usageNodes.add((VariableUsageNode) child); + } + } if (usageNodes.isEmpty()) { return; diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/QualifiedReferenceUpdateVariableUsage.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/QualifiedReferenceUpdateVariableUsage.java index 3598939ab2..109de3fed3 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/QualifiedReferenceUpdateVariableUsage.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/QualifiedReferenceUpdateVariableUsage.java @@ -22,6 +22,7 @@ import org.eclipse.lsp.cobol.common.message.MessageTemplate; import org.eclipse.lsp.cobol.common.model.NodeType; import org.eclipse.lsp.cobol.common.model.tree.Node; +import org.eclipse.lsp.cobol.common.model.tree.ProgramNode; import org.eclipse.lsp.cobol.common.model.tree.variable.QualifiedReferenceNode; import org.eclipse.lsp.cobol.common.model.tree.variable.VariableNode; import org.eclipse.lsp.cobol.common.model.tree.variable.VariableUsageNode; @@ -32,7 +33,9 @@ import org.eclipse.lsp.cobol.core.engine.symbols.SymbolAccumulatorService; import org.eclipse.lsp.cobol.common.model.tree.FigurativeConstants; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; /** QualifiedReferenceNode processor */ @@ -49,23 +52,22 @@ public QualifiedReferenceUpdateVariableUsage(SymbolAccumulatorService symbolAccu @Override public void accept(QualifiedReferenceNode node, ProcessingContext ctx) { - List variableUsageNodes = - node.getChildren().stream() - .filter(Node.hasType(NodeType.VARIABLE_USAGE)) - .map(VariableUsageNode.class::cast) - .collect(Collectors.toList()); + List variableUsageNodes = new ArrayList<>(); + for (Node child : node.getChildren()) { + if (child.getNodeType() == NodeType.VARIABLE_USAGE) { + variableUsageNodes.add((VariableUsageNode) child); + } + } if (variableUsageNodes.isEmpty()) { LOG.warn("Qualified reference node don't have any variable usages. {}", node); return; } - List foundDefinitions = - node.getProgram() - .map( - programNode -> - symbolAccumulatorService.getVariableDefinition(programNode, variableUsageNodes)) - .orElseGet(ImmutableList::of); + Optional program = node.getProgram(); + List foundDefinitions = program.map(programNode -> + symbolAccumulatorService.getVariableDefinition(programNode, variableUsageNodes)) + .orElseGet(ImmutableList::of); if (isQualifyExtendedDirectiveEnabled(ctx) && foundDefinitions.size() > 1) { foundDefinitions = updateDefinitionForQualifyExtended(node, foundDefinitions); diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTree.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTree.java index eb92420aed..7dce29af87 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTree.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/RootNodeUpdateCopyNodesByPositionInTree.java @@ -21,10 +21,10 @@ import org.eclipse.lsp.cobol.common.processor.Processor; import org.eclipse.lsp.cobol.common.model.tree.RootNode; import org.eclipse.lsp.cobol.common.utils.RangeUtils; -import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Range; import java.util.List; +import java.util.Objects; import static java.util.stream.Collectors.toList; @@ -38,22 +38,26 @@ public void accept(RootNode node, ProcessingContext ctx) { private void updateCopyNodes(RootNode node) { List nodes = node.getChildren().stream().filter(Node.hasType(NodeType.COPY)).collect(toList()); - nodes.forEach(node::removeChild); - nodes.forEach( - it -> { - Node parentNode = - RangeUtils.findNodeByPosition( - node, it.getLocality().getUri(), it.getLocality().getRange().getStart()) + node.getChildren().removeAll(nodes); + for (Node it : nodes) { + Node parentNode = RangeUtils.findNodeByPosition(node, + it.getLocality().getUri(), it.getLocality().getRange().getStart()) .orElse(node); - parentNode.addChildAt(getNodeInsertionIndex(parentNode.getChildren(), it), it); - }); - - List copyNodes = node.getDepthFirstStream() - .filter(Node.hasType(NodeType.COPY)) - .map(CopyNode.class::cast) - .collect(toList()); + parentNode.addChildAt(getNodeInsertionIndex(parentNode.getChildren(), it), it); + } - copyNodes.forEach(c -> registerCopyUsage(copyNodes, c.getNameLocation(), c.getUri())); + List copyNodes = node.getDepthFirstList(n -> + n.getNodeType() == NodeType.COPY && ((CopyNode) n).getUri() != null); + for (Node c : copyNodes) { + for (Node n : copyNodes) { + CopyNode cn1 = (CopyNode) c; + CopyNode cn2 = (CopyNode) n; + if (!Objects.equals(cn2.getNameLocation(), cn1.getNameLocation()) + && Objects.equals(cn2.getUri(), cn1.getUri())) { + cn2.addUsage(cn1.getNameLocation()); + } + } + } } private int getNodeInsertionIndex(List nodes, Node nodeToInsert) { @@ -82,11 +86,4 @@ private int getNodeInsertionIndex(List nodes, Node nodeToInsert) { return nodeSize; } - private void registerCopyUsage(List node, Location nameLocation, String uri) { - node.forEach(n -> { - if (n.getUri() != null && !n.getNameLocation().equals(nameLocation) && n.getUri().equals(uri)) { - n.addUsage(nameLocation); - } - }); - } } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/SectionNodeProcessorHelper.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/SectionNodeProcessorHelper.java index daf5fd9588..fc1cd88999 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/SectionNodeProcessorHelper.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/SectionNodeProcessorHelper.java @@ -90,7 +90,9 @@ public class SectionNodeProcessorHelper { */ public List processNodeWithVariableDefinitions(Node node) { Deque variableDefinitionNodes = new LinkedList<>(unwrapVariables(node)); - variableDefinitionNodes.forEach(n -> n.getParent().removeChild(n)); + for (VariableDefinitionNode n : variableDefinitionNodes) { + n.getParent().removeChild(n); + } List errors = new ArrayList<>(); errors.addAll(processDefinition(node, 1, variableDefinitionNodes)); errors.addAll(checkGlobalUniqueNames(node)); @@ -109,16 +111,14 @@ private List unwrapVariables(Node node) { List variables = new ArrayList<>(); List copybooks = new LinkedList<>(); - node.getChildren() - .forEach( - c -> { - if (c.getNodeType() == NodeType.VARIABLE_DEFINITION) { - variables.add(c); - } - if (c.getNodeType() == NodeType.COPY) { - copybooks.add((CopyNode) c); - } - }); + for (Node child : node.getChildren()) { + if (child.getNodeType() == NodeType.VARIABLE_DEFINITION) { + variables.add(child); + } + if (child.getNodeType() == NodeType.COPY) { + copybooks.add((CopyNode) child); + } + } copybooks.sort(Comparator.comparingInt(c -> c.getLocality().getRange().getStart().getLine())); @@ -173,7 +173,7 @@ private static boolean adjustCopyNodeChild(CopyNode copyNode, List variabl Node variable = nodes.get(i); String variableNodeUri = variable.getLocality().getUri(); String copybookNodeUri = copyNode.getUri(); - if (variableNodeUri.equals(copybookNodeUri)) { + if (Objects.equals(variableNodeUri, copybookNodeUri)) { adjustVariableNodeInsideCopyNode(copyNode, variables, i, variable); areNodesAdjusted = true; } else { @@ -205,11 +205,16 @@ private static void adjustVariableNodeInsideCopyNode(CopyNode copyNode, List n.getLocality().equals(variable.getLocality()) - && n.getVariableName().equals(variable.getVariableName())); + for (Node node : copyNode.getChildren()) { + if (node instanceof VariableDefinitionNode) { + VariableDefinitionNode varNode = (VariableDefinitionNode) node; + if (varNode.getLocality().equals(variable.getLocality()) + && varNode.getVariableName().equals(variable.getVariableName())) { + return true; + } + } + } + return false; } private static boolean canInsertCopyNodeAtIndex( @@ -324,12 +329,15 @@ private static Node getParentForNextLevel( } private Optional> convert(VariableDefinitionNode definitionNode) { - return matchers.stream() - .map(matcher -> matcher.apply(definitionNode)) - .filter(Objects::nonNull) - .map(result -> handleGeneralErrors(result, definitionNode)) - .map(result -> handleRedefines(result, definitionNode)) - .findFirst(); + for (Function> matcher : matchers) { + ResultWithErrors result = matcher.apply(definitionNode); + if (result != null) { + ResultWithErrors variableNodeResultWithErrors = handleGeneralErrors(result, definitionNode); + ResultWithErrors nodeResultWithErrors = handleRedefines(variableNodeResultWithErrors, definitionNode); + return Optional.of(nodeResultWithErrors); + } + } + return Optional.empty(); } private ResultWithErrors handleGeneralErrors( diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/implicit/BlkImplicitVariablesGenerator.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/implicit/BlkImplicitVariablesGenerator.java deleted file mode 100644 index e0b732e3d8..0000000000 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/processors/implicit/BlkImplicitVariablesGenerator.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2022 Broadcom. - * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Broadcom, Inc. - initial API and implementation - * - */ -package org.eclipse.lsp.cobol.core.engine.processors.implicit; - -import lombok.experimental.UtilityClass; -import org.eclipse.lsp.cobol.common.model.Locality; -import org.eclipse.lsp.cobol.common.model.tree.variable.ElementaryItemNode; -import org.eclipse.lsp.cobol.common.model.tree.variable.GroupItemNode; -import org.eclipse.lsp.cobol.common.model.tree.variable.UsageFormat; -import org.eclipse.lsp.cobol.common.model.tree.variable.VariableNode; -import org.eclipse.lsp.cobol.common.utils.ImplicitCodeUtils; - -/** - * Generates DFHEIBLK data structure - */ -@UtilityClass -public class BlkImplicitVariablesGenerator { - - /** - * Generates DFHEIBLK data structure - * @return DFHEIBLK data structure - */ - public VariableNode generate() { - // Add DFHEIBLK data structure - /* - 01 DFHEIBLK. - 02 EIBTIME COMP-3 PIC S9(7). - 02 EIBDATE COMP-3 PIC S9(7). - 02 EIBTRNID PIC X(4). - 02 EIBTASKN COMP-3 PIC S9(7). - 02 EIBTRMID PIC X(4). - 02 DFHEIGDI COMP PIC S9(4). - 02 EIBCPOSN COMP PIC S9(4). - 02 EIBCALEN COMP PIC S9(4). - 02 EIBAID PIC X(1). - 02 EIBFN PIC X(2). - 02 EIBRCODE PIC X(6). - 02 EIBDS PIC X(8). - 02 EIBREQID PIC X(8). - 02 EIBRSRCE PIC X(8). - 02 EIBSYNC PIC X(1). - 02 EIBFREE PIC X(1). - 02 EIBRECV PIC X(1). - 02 EIBFIL01 PIC X(1). - 02 EIBATT PIC X(1). - 02 EIBEOC PIC X(1). - 02 EIBFMH PIC X(1). - 02 EIBCOMPL PIC X(1). - 02 EIBSIG PIC X(1). - 02 EIBCONF PIC X(1). - 02 EIBERR PIC X(1). - 02 EIBERRCD PIC X(4). - 02 EIBSYNRB PIC X(1). - 02 EIBNODAT PIC X(1). - 02 EIBRESP COMP PIC S9(8). - 02 EIBRESP2 COMP PIC S9(8). - 02 EIBRLDBK PIC X(1). - */ - - Locality locality = Locality.builder() - .uri(ImplicitCodeUtils.createFullUrl("implicit-code-DFHEIBLK")) - .build(); - - VariableNode variable = new GroupItemNode(locality, 1, "DFHEIBLK", false, false, UsageFormat.UNDEFINED); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBTIME", false, "S9(7)", - null, UsageFormat.COMP_3, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBDATE", false, "S9(7)", - null, UsageFormat.COMP_3, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBTRNID", false, "X(4)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBTASKN", false, "S9(7)", - null, UsageFormat.COMP_3, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBTRMID", false, "X(4)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "DFHEIGDI", false, "S9(4)", - null, UsageFormat.COMP, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBCPOSN", false, "S9(4)", - null, UsageFormat.COMP, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBCALEN", false, "S9(4)", - null, UsageFormat.COMP, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBAID", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBFN", false, "X(2)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBRCODE", false, "X(6)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBDS", false, "X(8)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBREQID", false, "X(8)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBRSRCE", false, "X(8)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBSYNC", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBSYNC", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBFREE", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBRECV", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBFIL01", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBATT", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBEOC", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBFMH", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBCOMPL", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBSIG", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBCONF", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBERR", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBERRCD", false, "X(4)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBSYNRB", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBNODAT", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBRESP", false, "S9(8)", - null, UsageFormat.COMP, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBRESP2", false, "S9(8)", - null, UsageFormat.COMP, false, false, false)); - variable.addChild(new ElementaryItemNode(locality, 2, "EIBRLDBK", false, "X(1)", - null, UsageFormat.UNDEFINED, false, false, false)); - - return variable; - } -} diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/symbols/SymbolAccumulatorService.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/symbols/SymbolAccumulatorService.java index 62aa48335a..cf7afb265d 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/symbols/SymbolAccumulatorService.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/engine/symbols/SymbolAccumulatorService.java @@ -153,13 +153,15 @@ public Optional registerCodeBlockUsage( ProgramNode program, CodeBlockUsageNode node) { SymbolTable symbolTable = createOrGetSymbolTable(program); - List definitions = - symbolTable.getCodeBlocks().stream() - .filter(it -> filterNodes(it, node)) - .collect(Collectors.toList()); + List definitions = new ArrayList<>(); + for (CodeBlockDefinitionNode codeBlockDefinitionNode : symbolTable.getCodeBlocks()) { + if (filterNodes(codeBlockDefinitionNode, node)) { + definitions.add(codeBlockDefinitionNode); + } + } - if (definitions.isEmpty()) { - return Optional.of( + if (definitions.isEmpty()) { + return Optional.of( SyntaxError.syntaxError() .errorSource(ErrorSource.PARSING) .messageTemplate( @@ -476,25 +478,25 @@ public Map getProgramSymbols() { public List getVariableDefinition( ProgramNode programNode, List usageNodes) { Multimap variables = createOrGetSymbolTable(programNode).getVariables(); - List foundDefinitions = - VariableUsageUtils.findVariablesForUsage(variables, usageNodes); + List foundDefinitions = VariableUsageUtils.findVariablesForUsage(variables, usageNodes); if (!foundDefinitions.isEmpty()) { return foundDefinitions; } Multimap globals = ArrayListMultimap.create(); - getMapOfGlobalVariables(programNode) - .values() - .forEach(variableNode -> globals.put(variableNode.getName(), variableNode)); + for (VariableNode variableNode : getMapOfGlobalVariables(programNode).values()) { + globals.put(variableNode.getName(), variableNode); + } return VariableUsageUtils.findVariablesForUsage(globals, usageNodes); } private Map getMapOfGlobalVariables(ProgramNode programNode) { - Map result = - programNode.getProgram().map(this::getMapOfGlobalVariables).orElseGet(HashMap::new); - createOrGetSymbolTable(programNode).getVariables().values().stream() - .filter(VariableNode::isGlobal) - .forEach(variableNode -> result.put(variableNode.getName(), variableNode)); + Map result = programNode.getProgram().map(this::getMapOfGlobalVariables).orElseGet(HashMap::new); + for (VariableNode variableNode : createOrGetSymbolTable(programNode).getVariables().values()) { + if (variableNode.isGlobal()) { + result.put(variableNode.getName(), variableNode); + } + } return result; } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/model/VariableUsageUtils.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/model/VariableUsageUtils.java index 7eb4a57cbb..f0806a6b4c 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/model/VariableUsageUtils.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/model/VariableUsageUtils.java @@ -43,19 +43,17 @@ public class VariableUsageUtils { * @return the list of all matched variables */ public static List findVariablesForUsage( - Multimap definedVariables, List usageNodes) { - Map variableToStepCountsToMatchParentsMap = - findDefinedVariable(usageNodes.get(0).getName(), definedVariables).stream() - .map( - it -> - mapVariableToStepCountsToMatchParents( - it, usageNodes.subList(1, usageNodes.size()))) - .reduce( - (firstMap, secondMap) -> { - firstMap.putAll(secondMap); - return firstMap; - }) - .orElse(Collections.emptyMap()); + Multimap definedVariables, List usageNodes) { + Map acc = null; + for (VariableNode it : findDefinedVariable(usageNodes.get(0).getName(), definedVariables)) { + Map varMap = mapVariableToStepCountsToMatchParents(it, usageNodes.subList(1, usageNodes.size())); + if (acc == null) { + acc = varMap; + } else { + acc.putAll(varMap); + } + } + Map variableToStepCountsToMatchParentsMap = acc != null ? acc : Collections.emptyMap(); List exactHierarchyMatchedVariables = variableToStepCountsToMatchParentsMap.entrySet().stream() @@ -70,17 +68,16 @@ public static List findVariablesForUsage( private Collection findDefinedVariable(String name, Multimap definedVariables) { Collection foundVariable = definedVariables.get(name); - if (foundVariable.size() > 0) { + if (!foundVariable.isEmpty()) { return foundVariable; } - Optional node = definedVariables.values() - .stream().flatMap(Node::getDepthFirstStream) - .filter(VariableNode.class::isInstance) - .map(VariableNode.class::cast) - .filter(var -> var.getName().equals(name)) - .findFirst(); - if (node.isPresent()) { - foundVariable.add(node.get()); + + for (VariableNode node: definedVariables.values()) { + Node r = node.getDepthFirstFirstNode(n -> n instanceof VariableNode && ((VariableNode) n).getName().equals(name)); + if (r != null) { + foundVariable.add((VariableNode) r); + break; + } } return foundVariable; } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/CobolVisitor.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/CobolVisitor.java index 5b0d656f6c..aa26e7e037 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/CobolVisitor.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/CobolVisitor.java @@ -107,19 +107,17 @@ public CobolVisitor( public List visitStartRule(StartRuleContext ctx) { // we can skip the other nodes, but not the root try { - return ImmutableList.of( - retrieveLocality(ctx, extendedDocument, copybooks) - .map(RootNode::new) - .map( - rootNode -> { - visitChildren(ctx).forEach(rootNode::addChild); - return rootNode; - }) - .orElseGet( - () -> { - LOG.warn("The root node for syntax tree was not constructed"); - return new RootNode(); - })); + Optional localityOpt = retrieveLocality(ctx, extendedDocument, copybooks); + if (!localityOpt.isPresent()) { + LOG.warn("The root node for syntax tree was not constructed"); + return ImmutableList.of(new RootNode()); + } + + RootNode result = new RootNode(localityOpt.get()); + for (Node child: visitChildren(ctx)) { + result.addChild(child); + } + return ImmutableList.of(result); } finally { text.flush(); } @@ -1291,15 +1289,13 @@ public List visitConstantName(ConstantNameContext ctx) { @Override protected List defaultResult() { - return ImmutableList.of(); + return new ArrayList<>(); } @Override protected List aggregateResult(List aggregate, List nextResult) { - List result = new ArrayList<>(aggregate.size() + nextResult.size()); - result.addAll(aggregate); - result.addAll(nextResult); - return result; + aggregate.addAll(nextResult); + return aggregate; } /** @@ -1793,13 +1789,13 @@ private List retrieveIndexNames(DataOccursClauseContext } private List retrieveValues(List clauses) { - return clauses.stream().map(this::retrieveValue).collect(toList()); - } - - private ValueClause retrieveValue(DataValueClauseContext context) { - return new ValueClause( - retrieveValueIntervalsOld(context.dataValueClauseLiteral().dataValueInterval()), - getLocality(context.getStart()).orElse(null)); + List list = new ArrayList<>(); + for (DataValueClauseContext clause : clauses) { + List valueIntervals = retrieveValueIntervalsOld(clause.dataValueClauseLiteral().dataValueInterval()); + Locality locality = getLocality(clause.getStart()).orElse(null); + list.add(new ValueClause(valueIntervals, locality)); + } + return list; } private Locality getLevelLocality(TerminalNode terminalNode) { diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/MisspelledKeywordDistance.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/MisspelledKeywordDistance.java index 471b0a660c..bb69914f14 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/MisspelledKeywordDistance.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/MisspelledKeywordDistance.java @@ -18,6 +18,7 @@ import lombok.experimental.UtilityClass; import org.apache.commons.text.similarity.LevenshteinDistance; +import java.util.ArrayList; import java.util.Optional; import static java.util.Comparator.comparingInt; @@ -35,7 +36,7 @@ public class MisspelledKeywordDistance { public static final KeywordSuggestions KEYWORDS = new KeywordSuggestions(); private static final LevenshteinDistance DISTANCE = LevenshteinDistance.getDefaultInstance(); private static final List SORTED_KEYWORDS = KEYWORDS.getSuggestions().stream() - .sorted(comparingInt(s -> s.length())).collect(toList()); + .sorted(comparingInt(String::length)).collect(toList()); private static final int DIST_LIMIT = 2; /** @@ -45,12 +46,20 @@ public class MisspelledKeywordDistance { * @return the closest keyword or null if nothing found */ public Optional calculateDistance(String wrongToken) { - return SORTED_KEYWORDS.stream() - .filter(s -> Math.abs(s.length() - wrongToken.length()) < DIST_LIMIT) - .map(item -> new Object[] {item, DISTANCE.apply(wrongToken, item)}) - .filter(item -> (int) item[1] < DIST_LIMIT) - .sorted(comparingInt(o -> (int) o[1])) - .map(item -> item[0].toString()) - .findFirst(); + List toSort = new ArrayList<>(); + for (String s : SORTED_KEYWORDS) { + if (Math.abs(s.length() - wrongToken.length()) < DIST_LIMIT) { + Object[] item = new Object[]{s, DISTANCE.apply(wrongToken, s)}; + if ((int) item[1] < DIST_LIMIT) { + toSort.add(item); + } + } + } + toSort.sort(comparingInt(o -> (int) o[1])); + for (Object[] item : toSort) { + String string = item[0].toString(); + return Optional.of(string); + } + return Optional.empty(); } } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/VisitorHelper.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/VisitorHelper.java index 9e87ba2b7c..1878ee7c8b 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/VisitorHelper.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/core/visitor/VisitorHelper.java @@ -96,17 +96,6 @@ public static Locality getIntervalPosition(Locality start, Locality stop) { .build(); } - /** - * Extract picture texts - * - * @param clauses a list of ANTLR picture clauses - * @return the list of picture texts - */ - public static List retrievePicTexts(List clauses) { - return clauses.stream() - .map(clause -> clause.getText().replaceAll(clause.getStart().getText(), "").trim()) - .collect(toList()); - } /** * Extract picture texts * @@ -114,36 +103,13 @@ public static List retrievePicTexts(List claus * @return the list of picture texts */ public static List retrievePicTextsOld(List clauses) { - return clauses.stream() - .map(clause -> clause.getText().replaceAll(clause.getStart().getText(), "").trim()) - .collect(toList()); + List list = new ArrayList<>(clauses.size()); + for (CobolParser.DataPictureClauseContext clause : clauses) { + list.add(clause.getText().substring(clause.getStart().getText().length()).trim()); + } + return list; } - /** - * Extract value intervals. It's also applicable for raw values. In case of just a value `to` - * field will be `null`. - * - * @param contexts a list of ANTLR value intervals - * @return the list of value intervals - */ - public static List retrieveValueIntervals(List contexts) { - return contexts.stream() - .map( - context -> - new ValueInterval( - context.dataValueIntervalFrom().getText(), - ofNullable(context.dataValueIntervalTo()) - .map(DataValueIntervalToContext::literal) - .map(ParserRuleContext::getText) - .map(String::toUpperCase) - .orElse(null), - ofNullable(context.dataValueIntervalTo()) - .map(DataValueIntervalToContext::thruToken) - .map(ParserRuleContext::getText) - .map(String::toUpperCase) - .orElse(null))) - .collect(toList()); - } /** * Extract value intervals. It's also applicable for raw values. In case of just a value `to` * field will be `null`. @@ -152,39 +118,20 @@ public static List retrieveValueIntervals(List retrieveValueIntervalsOld(List contexts) { - return contexts.stream() - .map( - context -> - new ValueInterval( - context.dataValueIntervalFrom().getText(), - ofNullable(context.dataValueIntervalTo()) - .map(org.eclipse.lsp.cobol.core.CobolParser.DataValueIntervalToContext::literal) - .map(ParserRuleContext::getText) - .map(String::toUpperCase) - .orElse(null), - ofNullable(context.dataValueIntervalTo()) - .map(org.eclipse.lsp.cobol.core.CobolParser.DataValueIntervalToContext::thruToken) - .map(ParserRuleContext::getText) - .map(String::toUpperCase) - .orElse(null))) - .collect(toList()); + List list = new ArrayList<>(); + for (CobolParser.DataValueIntervalContext context: contexts) { + String from = context.dataValueIntervalFrom().getText(); + String to = context.dataValueIntervalTo() != null + ? context.dataValueIntervalTo().literal().getText().toUpperCase() + : null; + String thruToken = context.dataValueIntervalTo() != null + ? context.dataValueIntervalTo().thruToken().getText().toUpperCase() + : null; + list.add(new ValueInterval(from, to, thruToken)); + } + return list; } - /** - * Extract usage format from ANTLR usage clause - * - * @param contexts a list of ANTLR usage clauses - * @return the list of usage formats - */ - public static List retrieveUsageFormat(List contexts) { - return contexts.stream() - .map(DataUsageClauseContext::usageFormat) - .filter(Objects::nonNull) - .map(UsageFormatContext::getStart) - .map(Token::getText) - .map(UsageFormat::of) - .collect(toList()); - } /** * Extract usage format from ANTLR usage clause * @@ -307,20 +254,6 @@ public static Optional retrieveOccursToValueOld(CobolParser.DataOccursC .map(VisitorHelper::getIntegerOld); } - /** - * Gets value from ValueIsTokenContext context - * @param ctx a context object - * @return extracted value - */ - public static String retrieveValueToken(ValueIsTokenContext ctx) { - return ctx.valueToken().getText().toUpperCase() - + Optional.ofNullable(ctx.isAreToken()) - .map(ParserRuleContext::getText) - .map(String::toUpperCase) - .map(" "::concat) - .orElse(""); - } - /** * Gets value from ValueIsTokenContext context * @param ctx a context object @@ -363,9 +296,10 @@ public static Locality buildNameRangeLocality(ParserRuleContext ctx, String name * @return range object */ public static Range buildTokenRange(Token token) { + int line = token.getLine() - 1; + int tokenLen = token.getStopIndex() - token.getStartIndex() + 1; return new Range( - new Position(token.getLine() - 1, token.getCharPositionInLine()), - new Position(token.getLine() - 1, token.getCharPositionInLine() + token.getText().length())); + new Position(line, token.getCharPositionInLine()), + new Position(line, token.getCharPositionInLine() + tokenLen)); } - } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/dialects/ibm/TransformTreeStage.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/dialects/ibm/TransformTreeStage.java index a421ebd0ba..ff8285eec4 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/dialects/ibm/TransformTreeStage.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/dialects/ibm/TransformTreeStage.java @@ -141,7 +141,7 @@ protected List transformAST(AnalysisContext ctx, private void shapeSectionsAndParagraphs(Node parent) { LinkedList stack = new LinkedList<>(); for (Node node : parent.getChildren()) { - parent.removeChild(node); + parent.getChildren().remove(0); if (!node.getChildren().isEmpty()) { shapeSectionsAndParagraphs(node); } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/cics/CICSVisitor.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/cics/CICSVisitor.java index 487ebe54f8..ba55536c9b 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/cics/CICSVisitor.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/cics/CICSVisitor.java @@ -342,7 +342,7 @@ private void areaBWarning(@NonNull List tokenList) { private Locality getTokenLocality(Token token) { return Locality.builder() .uri(context.getProgramDocumentUri()) - .range(buildTokenRange(token)) + .range(VisitorHelper.buildTokenRange(token)) .build(); } @@ -375,19 +375,6 @@ private void throwException(String wrongToken, @NonNull Locality locality, Strin } } - /** - * Builds context name locality based on the name and uri of the document - * - * @param token is a token - * @return range object - */ - private Range buildTokenRange(Token token) { - return new Range( - new Position(token.getLine() - 1, token.getCharPositionInLine()), - new Position( - token.getLine() - 1, token.getCharPositionInLine() + token.getText().length())); - } - private Range buildTokenEndRange(Token token) { Position p = new Position(token.getLine() - 1, token.getCharPositionInLine() + token.getStopIndex() - token.getStartIndex() + 1); return new Range(p, p); diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/cics/processor/CICSTranslateMandatorySectionProcess.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/cics/processor/CICSTranslateMandatorySectionProcess.java index 30de441041..7da2b366d7 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/cics/processor/CICSTranslateMandatorySectionProcess.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/cics/processor/CICSTranslateMandatorySectionProcess.java @@ -83,10 +83,12 @@ private void insertWorkingStorageSection(ProgramNode programNode) { } private boolean sectionExists(ProgramNode programNode, SectionType sectionType) { - return programNode - .getDepthFirstStream() - .filter(SectionNode.class::isInstance) - .map(SectionNode.class::cast) - .anyMatch(node -> node.getSectionType() == sectionType); + return null != programNode.getDepthFirstFirstNode( + n -> n instanceof SectionNode && ((SectionNode) n).getSectionType() == sectionType); +// return programNode +// .getDepthFirstStream() +// .filter(SectionNode.class::isInstance) +// .map(SectionNode.class::cast) +// .anyMatch(node -> node.getSectionType() == sectionType); } } diff --git a/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/processor/ImplicitDb2VariablesProcessor.java b/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/processor/ImplicitDb2VariablesProcessor.java index 8985e4d7f1..59df49b7f3 100644 --- a/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/processor/ImplicitDb2VariablesProcessor.java +++ b/server/engine/src/main/java/org/eclipse/lsp/cobol/implicitDialects/sql/processor/ImplicitDb2VariablesProcessor.java @@ -65,14 +65,19 @@ private static String getSqlBackendConfig(ProcessingContext processingContext) { } private static boolean hasSqlCa(ProgramNode programNode) { - return programNode - .getDepthFirstStream() - .anyMatch( - node -> - node.getNodeType().equals(NodeType.VARIABLE) - && ((VariableNode) node).getName().equalsIgnoreCase("SQLCA") - && (node instanceof VariableWithLevelNode) - && ((VariableWithLevelNode) node).getLevel() == 1); + return null != programNode.getDepthFirstFirstNode(node -> { + if (!node.getNodeType().equals(NodeType.VARIABLE)) { + return false; + } + if (!(node instanceof VariableWithLevelNode)) { + return false; + } + VariableWithLevelNode vwl = (VariableWithLevelNode) node; + if (vwl.getLevel() != 1) { + return false; + } + return vwl.getName().equalsIgnoreCase("SQLCA"); + }); } private void registerVariables( diff --git a/server/engine/src/test/java/org/eclipse/lsp/cobol/core/model/tree/logic/implicit/BlkImplicitVariablesGeneratorTest.java b/server/engine/src/test/java/org/eclipse/lsp/cobol/core/model/tree/logic/implicit/BlkImplicitVariablesGeneratorTest.java index 4166f0929c..9bcfdcd82f 100644 --- a/server/engine/src/test/java/org/eclipse/lsp/cobol/core/model/tree/logic/implicit/BlkImplicitVariablesGeneratorTest.java +++ b/server/engine/src/test/java/org/eclipse/lsp/cobol/core/model/tree/logic/implicit/BlkImplicitVariablesGeneratorTest.java @@ -15,7 +15,7 @@ package org.eclipse.lsp.cobol.core.model.tree.logic.implicit; import org.eclipse.lsp.cobol.common.model.tree.variable.VariableNode; -import org.eclipse.lsp.cobol.core.engine.processors.implicit.BlkImplicitVariablesGenerator; +import org.eclipse.lsp.cobol.implicitDialects.cics.generator.CICSBulkImplicitVariablesGenerator; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -26,7 +26,7 @@ class BlkImplicitVariablesGeneratorTest { @Test void test() { - VariableNode variableNode = BlkImplicitVariablesGenerator.generate(); + VariableNode variableNode = CICSBulkImplicitVariablesGenerator.generate(); assertEquals("DFHEIBLK", variableNode.getName()); } }