Skip to content

Commit 44db78d

Browse files
maksim-grebeniuk-sonarsourcesonartech
authored andcommitted
SONARIAC-2056 S7020 should not raise on RUN instruction on heredoc form with comments (#674)
GitOrigin-RevId: 299c6b42b49fef2f4f2d218a56cf3e38b71c116b
1 parent 3890f96 commit 44db78d

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

iac-extensions/docker/src/main/java/org/sonar/iac/docker/checks/LongRunInstructionCheck.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.net.URISyntaxException;
2121
import java.net.URL;
2222
import java.util.ArrayList;
23+
import java.util.Collection;
2324
import java.util.HashMap;
2425
import java.util.List;
2526
import java.util.Map;
@@ -32,10 +33,12 @@
3233
import org.sonar.iac.common.api.checks.CheckContext;
3334
import org.sonar.iac.common.api.checks.IacCheck;
3435
import org.sonar.iac.common.api.checks.InitContext;
36+
import org.sonar.iac.common.api.tree.HasComments;
3537
import org.sonar.iac.common.api.tree.Tree;
3638
import org.sonar.iac.common.api.tree.impl.TextRanges;
3739
import org.sonar.iac.common.extension.visitors.TreeContext;
3840
import org.sonar.iac.common.extension.visitors.TreeVisitor;
41+
import org.sonar.iac.docker.tree.TreeUtils;
3942
import org.sonar.iac.docker.tree.api.Argument;
4043
import org.sonar.iac.docker.tree.api.ArgumentList;
4144
import org.sonar.iac.docker.tree.api.ExpandableStringCharacters;
@@ -114,6 +117,7 @@ private void processShellCode(ShellCode shellCode, RunInstruction runInstruction
114117
if (originalSourceCode == null) {
115118
return;
116119
}
120+
var commentsByLine = getCommentLengthsByLine(shellCode);
117121
var codeLines = originalSourceCode.lines().toList();
118122
for (int i = 0; i < codeLines.size(); i++) {
119123
var line = codeLines.get(i);
@@ -132,12 +136,22 @@ private void processShellCode(ShellCode shellCode, RunInstruction runInstruction
132136
// lineLength is only the part belonging to ShellCode; add the length of preceding parts of the Run instruction
133137
lineLength += shellCode.textRange().start().lineOffset();
134138
}
139+
lineLength -= commentsByLine.getOrDefault(lineNumber, 0);
135140
if (lineLength > maxLength) {
141+
136142
runInstructionData.tooLongLinesWithLastOffset.put(lineNumber, lineLength);
137143
}
138144
}
139145
}
140146

147+
private static Map<Integer, Integer> getCommentLengthsByLine(Tree from) {
148+
return TreeUtils.descendants(from).filter(HasComments.class::isInstance)
149+
.map(HasComments.class::cast)
150+
.map(HasComments::comments)
151+
.flatMap(Collection::stream)
152+
.collect(Collectors.toMap(comment -> comment.textRange().start().line(), comment -> comment.contentText().length(), (c1, c2) -> c1));
153+
}
154+
141155
private void processSyntaxToken(SyntaxToken token, RunInstructionData runInstructionData) {
142156
if (token.parent() instanceof SyntaxTokenShellCode) {
143157
// Skip tokens that are part of ShellCode, as they are already handled by processShellCode

iac-extensions/docker/src/main/java/org/sonar/iac/docker/tree/TreeUtils.java

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
*/
1717
package org.sonar.iac.docker.tree;
1818

19+
import java.util.ArrayDeque;
20+
import java.util.Iterator;
21+
import java.util.NoSuchElementException;
1922
import java.util.Objects;
2023
import java.util.Optional;
21-
import java.util.Spliterator;
2224
import java.util.Spliterators;
2325
import java.util.function.Predicate;
2426
import java.util.stream.Stream;
@@ -48,13 +50,40 @@ public static Optional<Tree> lastDescendant(@Nullable Tree root, Predicate<Tree>
4850
return descendants(root).filter(predicate).reduce((first, second) -> second);
4951
}
5052

51-
private static Stream<Tree> descendants(@Nullable Tree root) {
52-
if (root == null || root.children().isEmpty()) {
53-
return Stream.empty();
53+
public static Stream<Tree> descendants(@Nullable Tree root) {
54+
if (root == null) {
55+
return Stream.of();
5456
}
55-
Spliterator<Tree> spliterator = Spliterators.spliteratorUnknownSize(root.children().iterator(), Spliterator.ORDERED);
56-
Stream<Tree> stream = StreamSupport.stream(spliterator, false);
57-
return stream.flatMap(tree -> Stream.concat(Stream.of(tree), descendants(tree)));
57+
// A queue to hold the nodes to visit, maintaining BFS order
58+
var queue = new ArrayDeque<Tree>();
59+
queue.add(root);
60+
61+
// Create an Iterator based on the BFS logic
62+
var iterator = new Iterator<Tree>() {
63+
@Override
64+
public boolean hasNext() {
65+
return !queue.isEmpty();
66+
}
67+
68+
@Override
69+
public Tree next() {
70+
// Get the next node
71+
var node = queue.poll();
72+
if (node == null) {
73+
throw new NoSuchElementException();
74+
}
75+
76+
// Add all children of the current node to the queue for later processing
77+
queue.addAll(node.children());
78+
return node;
79+
}
80+
};
81+
82+
// Create a Spliterator from the iterator, specifying it's unordered,
83+
// non-null, and finite, then convert it to a sequential stream.
84+
return StreamSupport.stream(
85+
Spliterators.spliteratorUnknownSize(iterator, 0),
86+
false);
5887
}
5988

6089
public static Optional<DockerTree> firstAncestor(DockerTree node, Predicate<DockerTree> predicate) {

0 commit comments

Comments
 (0)