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,14 +19,12 @@
*/
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.jetbrains.python.PyStubElementTypes;
import com.jetbrains.python.psi.PyClass;
import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.python.PythonCheck;
import org.sonar.python.api.PythonGrammar;
import org.sonar.python.metrics.ComplexityVisitor;

@Rule(key = "ClassComplexity")
Expand All @@ -38,18 +36,18 @@ public class ClassComplexityCheck extends PythonCheck {
int maximumClassComplexityThreshold = DEFAULT_MAXIMUM_CLASS_COMPLEXITY_THRESHOLD;

@Override
public Set<AstNodeType> subscribedKinds() {
return Collections.singleton(PythonGrammar.CLASSDEF);
}

@Override
public void visitNode(AstNode node) {
int complexity = ComplexityVisitor.complexity(node);
if (complexity > maximumClassComplexityThreshold) {
String message = String.format(MESSAGE, complexity, maximumClassComplexityThreshold);
addIssue(node.getFirstChild(PythonGrammar.CLASSNAME), message)
.withCost(complexity - maximumClassComplexityThreshold);
}
public void initialize(Context context) {
context.registerSyntaxNodeConsumer(PyStubElementTypes.CLASS_DECLARATION, ctx -> {
PyClass node = (PyClass) ctx.syntaxNode();
int complexity = ComplexityVisitor.complexity(node);
if (complexity > maximumClassComplexityThreshold) {
String message = String.format(MESSAGE, complexity, maximumClassComplexityThreshold);
ASTNode nameNode = node.getNameNode();
if (nameNode != null) {
ctx.addIssue(nameNode.getPsi(), message).withCost(complexity - maximumClassComplexityThreshold);
}
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,22 @@
*/
package org.sonar.python.checks;

import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.AstNodeType;
import com.intellij.lang.ASTNode;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyFunction;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.python.IssueLocation;
import org.sonar.python.PythonCheck;
import org.sonar.python.api.PythonGrammar;
import org.sonar.python.metrics.CognitiveComplexityVisitor;

@Rule(key = CognitiveComplexityFunctionCheck.CHECK_KEY)
@Rule(key = "S3776")
public class CognitiveComplexityFunctionCheck extends PythonCheck {

private static final String MESSAGE = "Refactor this function to reduce its Cognitive Complexity from %s to the %s allowed.";
public static final String CHECK_KEY = "S3776";
private static final int DEFAULT_THRESHOLD = 15;

@RuleProperty(
Expand All @@ -49,23 +48,24 @@ public void setThreshold(int threshold) {
}

@Override
public Set<AstNodeType> subscribedKinds() {
return immutableSet(PythonGrammar.FUNCDEF);
}

@Override
public void visitNode(AstNode astNode) {
if (astNode.hasAncestor(PythonGrammar.FUNCDEF)) {
return;
}
List<IssueLocation> secondaryLocations = new ArrayList<>();
int complexity = CognitiveComplexityVisitor.complexity(astNode, (node, message) -> secondaryLocations.add(IssueLocation.preciseLocation(node, message)));
if (complexity > threshold){
String message = String.format(MESSAGE, complexity, threshold);
PreciseIssue issue = addIssue(astNode.getFirstChild(PythonGrammar.FUNCNAME), message)
.withCost(complexity - threshold);
secondaryLocations.forEach(issue::secondary);
}
public void initialize(Context context) {
context.registerSyntaxNodeConsumer(PyElementTypes.FUNCTION_DECLARATION, ctx -> {
PyFunction function = (PyFunction) ctx.syntaxNode();
if (PsiTreeUtil.getParentOfType(function, PyFunction.class) != null) {
return;
}
List<IssueLocation> secondaryLocations = new ArrayList<>();
int complexity = CognitiveComplexityVisitor.complexity(function, (node, message) -> secondaryLocations.add(IssueLocation.preciseLocation(node, message)));
if (complexity > threshold){
String message = String.format(MESSAGE, complexity, threshold);
ASTNode nameNode = function.getNameNode();
if (nameNode != null) {
PreciseIssue issue = ctx.addIssue(nameNode.getPsi(), message)
.withCost(complexity - threshold);
secondaryLocations.forEach(issue::secondary);
}
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
*/
package org.sonar.python.checks;

import com.sonar.sslr.api.AstNode;
import java.text.MessageFormat;
import com.jetbrains.python.psi.PyFileElementType;
import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.python.PythonCheck;
Expand All @@ -36,16 +35,18 @@ public class FileComplexityCheck extends PythonCheck {
int maximumFileComplexityThreshold = DEFAULT_MAXIMUM_FILE_COMPLEXITY_THRESHOLD;

@Override
public void leaveFile(AstNode astNode) {
int complexity = ComplexityVisitor.complexity(astNode);
if (complexity > maximumFileComplexityThreshold) {
String message = MessageFormat.format(
"File has a complexity of {0,number,integer} which is greater than {1,number,integer} authorized.",
complexity,
maximumFileComplexityThreshold);
addFileIssue(message)
.withCost(complexity - maximumFileComplexityThreshold);
}
public void initialize(Context context) {
context.registerSyntaxNodeConsumer(PyFileElementType.INSTANCE, ctx -> {
int complexity = ComplexityVisitor.complexity(ctx.syntaxNode());
if (complexity > maximumFileComplexityThreshold) {
String message = String.format(
"File has a complexity of %s which is greater than %s authorized.",
complexity,
maximumFileComplexityThreshold);
ctx.addFileIssue(message).withCost(complexity - maximumFileComplexityThreshold);
}
});
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@
*/
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.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyFunction;
import org.sonar.check.Rule;
import org.sonar.check.RuleProperty;
import org.sonar.python.PythonCheck;
import org.sonar.python.api.PythonGrammar;
import org.sonar.python.metrics.ComplexityVisitor;

@Rule(key = "FunctionComplexity")
Expand All @@ -40,18 +38,18 @@ public class FunctionComplexityCheck extends PythonCheck {
int maximumFunctionComplexityThreshold = DEFAULT_MAXIMUM_FUNCTION_COMPLEXITY_THRESHOLD;

@Override
public Set<AstNodeType> subscribedKinds() {
return Collections.singleton(PythonGrammar.FUNCDEF);
}

@Override
public void visitNode(AstNode node) {
int complexity = ComplexityVisitor.complexity(node);
if (complexity > maximumFunctionComplexityThreshold) {
String message = String.format(MESSAGE, complexity, maximumFunctionComplexityThreshold);
addIssue(node.getFirstChild(PythonGrammar.FUNCNAME), message)
.withCost(complexity - maximumFunctionComplexityThreshold);
}
public void initialize(Context context) {
context.registerSyntaxNodeConsumer(PyElementTypes.FUNCTION_DECLARATION, ctx -> {
PyFunction node = (PyFunction) ctx.syntaxNode();
int complexity = ComplexityVisitor.complexity(node);
if (complexity > maximumFunctionComplexityThreshold) {
String message = String.format(MESSAGE, complexity, maximumFunctionComplexityThreshold);
ASTNode nameNode = node.getNameNode();
if (nameNode != null) {
ctx.addIssue(nameNode.getPsi(), message).withCost(complexity - maximumFunctionComplexityThreshold);
}
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,14 @@ public void addIssue(PreciseIssue issue) {
issues.add(issue);
}

public void addIssue(PythonCheck check, PsiElement element, @Nullable String message) {
issues.add(new PreciseIssue(check, IssueLocation.preciseLocation(element, message)));
public PreciseIssue addIssue(PythonCheck check, IssueLocation issueLocation) {
PreciseIssue issue = new PreciseIssue(check, issueLocation);
issues.add(issue);
return issue;
}

public PreciseIssue addIssue(PythonCheck check, PsiElement element, @Nullable String message) {
return addIssue(check, IssueLocation.preciseLocation(element, message));
}

public List<PreciseIssue> getIssues() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@

import com.intellij.psi.PsiElement;
import javax.annotation.Nullable;
import org.sonar.python.PythonCheck.PreciseIssue;

public interface SubscriptionContext {

PsiElement syntaxNode();

void addIssue(PsiElement element, @Nullable String message);
PreciseIssue addIssue(PsiElement element, @Nullable String message);

PreciseIssue addFileIssue(@Nullable String message);

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Map;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import org.sonar.python.PythonCheck.PreciseIssue;

public class SubscriptionVisitor extends PyRecursiveElementVisitor {

Expand Down Expand Up @@ -83,9 +84,13 @@ public PsiElement syntaxNode() {
}

@Override
public void addIssue(PsiElement element, @Nullable String message) {
pythonVisitorContext.addIssue(check, element, message);
public PreciseIssue addIssue(PsiElement element, @Nullable String message) {
return pythonVisitorContext.addIssue(check, element, message);
}

@Override
public PreciseIssue addFileIssue(@Nullable String message) {
return pythonVisitorContext.addIssue(check, IssueLocation.atFileLevel(message));
}
}
}
Loading