Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
*/
package org.sonar.python.checks;

import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.AstNodeType;
import java.util.Collections;
import java.util.Set;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyStringLiteralExpression;
import org.sonar.check.Rule;
import org.sonar.python.PythonCheck;
import org.sonar.python.api.PythonTokenType;
import org.sonar.python.SubscriptionContext;

@Rule(key = "S1717")
public class BackslashInStringCheck extends PythonCheck {
Expand All @@ -34,13 +34,17 @@ public class BackslashInStringCheck extends PythonCheck {
private static final String VALID_ESCAPED_CHARACTERS = "abfnrtvxnNrtuU\\'\"0123456789\n\r";

@Override
public Set<AstNodeType> subscribedKinds() {
return Collections.singleton(PythonTokenType.STRING);
public void initialize(Context context) {
context.registerSyntaxNodeConsumer(PyElementTypes.STRING_LITERAL_EXPRESSION, ctx -> {
PyStringLiteralExpression expression = (PyStringLiteralExpression) ctx.syntaxNode();
for (ASTNode stringNode : expression.getStringNodes()) {
checkLiteral(ctx, stringNode.getPsi());
}
});
}

@Override
public void visitNode(AstNode node) {
String string = node.getTokenOriginalValue();
public void checkLiteral(SubscriptionContext ctx, PsiElement literal) {
String string = literal.getNode().getText();
int length = string.length();
boolean isEscaped = false;
boolean inPrefix = true;
Expand All @@ -54,7 +58,7 @@ public void visitNode(AstNode node) {
}
} else {
if (isEscaped && VALID_ESCAPED_CHARACTERS.indexOf(c) == -1 && !isBackslashedSpaceAfterInlineMarkup(isThreeQuotes, string, i, c)) {
addIssue(node, MESSAGE);
ctx.addIssue(literal, MESSAGE);
}
isEscaped = c == '\\' && !isEscaped;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,54 +19,53 @@
*/
package org.sonar.python.checks;

import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.AstNodeType;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyIfPart;
import com.jetbrains.python.psi.PyIfStatement;
import com.jetbrains.python.psi.PyStatement;
import com.jetbrains.python.psi.PyStatementList;
import javax.annotation.CheckForNull;
import org.sonar.check.Rule;
import org.sonar.python.PythonCheck;
import org.sonar.python.api.PythonGrammar;
import org.sonar.python.api.PythonKeyword;
import org.sonar.sslr.ast.AstSelect;

@Rule(key = CollapsibleIfStatementsCheck.CHECK_KEY)
@Rule(key = "S1066")
public class CollapsibleIfStatementsCheck extends PythonCheck {
public static final String CHECK_KEY = "S1066";
private static final String MESSAGE = "Merge this if statement with the enclosing one.";

@Override
public Set<AstNodeType> subscribedKinds() {
return Collections.singleton(PythonGrammar.IF_STMT);
}
public void initialize(Context context) {
context.registerSyntaxNodeConsumer(PyElementTypes.IF_STATEMENT, ctx -> {
PyIfStatement ifStatement = (PyIfStatement) ctx.syntaxNode();

@Override
public void visitNode(AstNode node) {
AstNode suite = node.getLastChild(PythonGrammar.SUITE);
if (suite.getPreviousSibling().getPreviousSibling().is(PythonKeyword.ELSE)) {
return;
}
AstNode singleIfChild = singleIfChild(suite);
if (singleIfChild != null && !hasElseOrElif(singleIfChild)) {
addIssue(singleIfChild.getToken(), MESSAGE)
.secondary(node.getFirstChild(), "enclosing");
}
}
if (ifStatement.getElsePart() != null) {
return;
}

PyIfPart[] elifParts = ifStatement.getElifParts();
PyIfPart lastIfPart = elifParts.length == 0 ? ifStatement.getIfPart() : elifParts[elifParts.length - 1];

private static boolean hasElseOrElif(AstNode ifNode) {
return ifNode.hasDirectChildren(PythonKeyword.ELIF) || ifNode.hasDirectChildren(PythonKeyword.ELSE);
PyIfStatement singleIfChild = singleIfChild(lastIfPart.getStatementList());
if (singleIfChild != null && singleIfChild.getElifParts().length == 0 && singleIfChild.getElsePart() == null) {
ctx.addIssue(ifKeyword(singleIfChild), MESSAGE)
.secondary(ifKeyword(ifStatement), "enclosing");
}
});
}

private static AstNode singleIfChild(AstNode suite) {
List<AstNode> statements = suite.getChildren(PythonGrammar.STATEMENT);
if (statements.size() == 1) {
AstSelect nestedIf = statements.get(0).select()
.children(PythonGrammar.COMPOUND_STMT)
.children(PythonGrammar.IF_STMT);
if (nestedIf.size() == 1) {
return nestedIf.get(0);
@CheckForNull
private static PyIfStatement singleIfChild(PyStatementList statementList) {
PyStatement[] statements = statementList.getStatements();
if (statements.length == 1) {
PyStatement statement = statements[0];
if (statement.getNode().getElementType() == PyElementTypes.IF_STATEMENT) {
return (PyIfStatement) statement;
}
}
return null;
}

private static PsiElement ifKeyword(PyIfStatement ifStatement) {
return ifStatement.getNode().findLeafElementAt(0).getPsi();
}
}
4 changes: 4 additions & 0 deletions python-checks/src/test/resources/checks/backslashInString.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@
z = "*a*\ s" # Noncompliant
z = """\ s""" # Noncompliant
z = ""
re.compile(r'...'
r'\("abc '
'def"\)$') # Noncompliant

6 changes: 6 additions & 0 deletions python-squid/src/main/java/org/sonar/python/PythonCheck.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
package org.sonar.python;

import com.intellij.psi.PsiElement;
import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.Token;
import java.util.ArrayList;
Expand Down Expand Up @@ -96,6 +97,11 @@ public PreciseIssue secondary(IssueLocation issueLocation) {
return this;
}

public PreciseIssue secondary(PsiElement element, @Nullable String message) {
secondaryLocations.add(IssueLocation.preciseLocation(element, message));
return this;
}

public List<IssueLocation> secondaryLocations() {
return secondaryLocations;
}
Expand Down