public static Result methodComplexity(MethodTree methodTree) { if (shouldAnalyzeMethod(methodTree)) { CognitiveComplexityVisitor visitor = new CognitiveComplexityVisitor(); methodTree.accept(visitor); return new Result(visitor.complexity, visitor.locations); } return Result.empty(); }
private void increaseComplexityByOne(Tree tree) { increaseComplexity(tree, 1); }
@Override public void visitTryStatement(TryStatementTree tree) { scan(tree.resourceList()); scan(tree.block()); tree.catches().forEach(c -> increaseComplexityByNesting(c.catchKeyword())); nesting++; scan(tree.catches()); nesting--; scan(tree.finallyBlock()); }
private static boolean shouldAnalyzeMethod(MethodTree methodTree) { return methodTree.block() != null && !memberOfAnonymousClass(methodTree) && !isWithinLocalClass(methodTree); }
@Override public void visitIfStatement(IfStatementTree tree) { increaseComplexityByNesting(tree.ifKeyword()); scan(tree.condition()); nesting++; scan(tree.thenStatement()); nesting--; boolean elseStatementNotIF = tree.elseStatement() != null && !tree.elseStatement().is(IF_STATEMENT); if (elseStatementNotIF) { increaseComplexityByOne(tree.elseKeyword()); nesting++; } else if (tree.elseStatement() != null) { // else statement is an if, visiting it will increase complexity by nesting so by one only. ignoreNesting = true; complexity -= nesting - 1; } scan(tree.elseStatement()); if (elseStatementNotIF) { nesting--; } }
@Override public void visitForStatement(ForStatementTree tree) { increaseComplexityByNesting(tree.forKeyword()); nesting++; super.visitForStatement(tree); nesting--; }
@Override public void visitBinaryExpression(BinaryExpressionTree tree) { if (tree.is(CONDITIONAL_AND, CONDITIONAL_OR) && !ignored.contains(tree)) { List<BinaryExpressionTree> flattenedLogicalExpressions = flattenLogicalExpression(tree).collect(Collectors.toList()); BinaryExpressionTree previous = null; for (BinaryExpressionTree current : flattenedLogicalExpressions) { if (previous == null || !previous.is(current.kind())) { increaseComplexityByOne(current.operatorToken()); } previous = current; } } super.visitBinaryExpression(tree); }
@Override public void visitBreakStatement(BreakStatementTree tree) { if (tree.label() != null) { increaseComplexityByOne(tree.breakKeyword()); } super.visitBreakStatement(tree); }
private Stream<BinaryExpressionTree> flattenLogicalExpression(ExpressionTree expression) { if (expression.is(CONDITIONAL_AND, CONDITIONAL_OR)) { ignored.add(expression); BinaryExpressionTree binaryExpr = (BinaryExpressionTree) expression; ExpressionTree left = ExpressionUtils.skipParentheses(binaryExpr.leftOperand()); ExpressionTree right = ExpressionUtils.skipParentheses(binaryExpr.rightOperand()); return Stream.concat(Stream.concat(flattenLogicalExpression(left), Stream.of(binaryExpr)), flattenLogicalExpression(right)); } return Stream.empty(); }
@Override public void visitNode(Tree tree) { MethodTree method = (MethodTree) tree; CognitiveComplexityVisitor.Result result = CognitiveComplexityVisitor.methodComplexity(method); int total = result.complexity; if (total > max) { reportIssue(method.simpleName(), "Refactor this method to reduce its Cognitive Complexity from " + total + " to the " + max + " allowed.", result.locations, total - max); } }
@Override public void scanFile(JavaFileScannerContext context) { sonarFile = fs.inputFile(fs.predicates().is(context.getFile())); CommentLinesVisitor commentLinesVisitor = createCommentLineVisitorAndFindNoSonar(context); if(isSonarLintContext()) { // No need to compute metrics on SonarLint side, but the no sonar filter is still required return; } classTrees.clear(); methods = 0; complexityInMethods = 0; classes = 0; methodComplexityDistribution = new RangeDistributionBuilder(LIMITS_COMPLEXITY_METHODS); super.setContext(context); scanTree(context.getTree()); //leave file. int fileComplexity = context.getComplexityNodes(context.getTree()).size(); saveMetricOnFile(CoreMetrics.CLASSES, classes); saveMetricOnFile(CoreMetrics.FUNCTIONS, methods); saveMetricOnFile(CoreMetrics.COMPLEXITY_IN_FUNCTIONS, complexityInMethods); saveMetricOnFile(CoreMetrics.COMPLEXITY_IN_CLASSES, fileComplexity); saveMetricOnFile(CoreMetrics.COMPLEXITY, fileComplexity); saveMetricOnFile(CoreMetrics.COMMENT_LINES, commentLinesVisitor.commentLinesMetric()); saveMetricOnFile(CoreMetrics.STATEMENTS, new StatementVisitor().numberOfStatements(context.getTree())); saveMetricOnFile(CoreMetrics.NCLOC, new LinesOfCodeVisitor().linesOfCode(context.getTree())); saveMetricOnFile(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION, methodComplexityDistribution.build()); RangeDistributionBuilder fileComplexityDistribution = new RangeDistributionBuilder(LIMITS_COMPLEXITY_FILES); saveMetricOnFile(CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION, fileComplexityDistribution.add(fileComplexity).build()); saveMetricOnFile(CoreMetrics.COGNITIVE_COMPLEXITY, CognitiveComplexityVisitor.compilationUnitComplexity(context.getTree())); }
@Override public void visitIfStatement(IfStatementTree tree) { increaseComplexityByNesting(tree.ifKeyword()); scan(tree.condition()); nesting++; scan(tree.thenStatement()); nesting--; boolean elseStatementNotIF = tree.elseStatement() != null && !tree.elseStatement().is(IF_STATEMENT); if (elseStatementNotIF) { increaseComplexityByOne(tree.elseKeyword()); nesting++; } else if (tree.elseStatement() != null) { // else statement is an if, visiting it will increase complexity by nesting so by one only. ignoreNesting = true; complexity -= nesting - 1; } scan(tree.elseStatement()); if (elseStatementNotIF) { nesting--; } }
@Override public void visitForEachStatement(ForEachStatement tree) { increaseComplexityByNesting(tree.forKeyword()); nesting++; super.visitForEachStatement(tree); nesting--; }
private static boolean shouldAnalyzeMethod(MethodTree methodTree) { return methodTree.block() != null && !memberOfAnonymousClass(methodTree) && !isWithinLocalClass(methodTree); }
@Override public void visitBinaryExpression(BinaryExpressionTree tree) { if (tree.is(CONDITIONAL_AND, CONDITIONAL_OR) && !ignored.contains(tree)) { List<BinaryExpressionTree> flattenedLogicalExpressions = flattenLogicalExpression(tree).collect(Collectors.toList()); BinaryExpressionTree previous = null; for (BinaryExpressionTree current : flattenedLogicalExpressions) { if (previous == null || !previous.is(current.kind())) { increaseComplexityByOne(current.operatorToken()); } previous = current; } } super.visitBinaryExpression(tree); }
@Override public void visitContinueStatement(ContinueStatementTree tree) { if (tree.label() != null) { increaseComplexityByOne(tree.continueKeyword()); } super.visitContinueStatement(tree); }
private Stream<BinaryExpressionTree> flattenLogicalExpression(ExpressionTree expression) { if (expression.is(CONDITIONAL_AND, CONDITIONAL_OR)) { ignored.add(expression); BinaryExpressionTree binaryExpr = (BinaryExpressionTree) expression; ExpressionTree left = ExpressionUtils.skipParentheses(binaryExpr.leftOperand()); ExpressionTree right = ExpressionUtils.skipParentheses(binaryExpr.rightOperand()); return Stream.concat(Stream.concat(flattenLogicalExpression(left), Stream.of(binaryExpr)), flattenLogicalExpression(right)); } return Stream.empty(); }
@Override public void visitNode(Tree tree) { MethodTree method = (MethodTree) tree; CognitiveComplexityVisitor.Result result = CognitiveComplexityVisitor.methodComplexity(method); int total = result.complexity; if (total > max) { reportIssue(method.simpleName(), "Refactor this method to reduce its Cognitive Complexity from " + total + " to the " + max + " allowed.", result.locations, total - max); } }
@Override public void scanFile(JavaFileScannerContext context) { sonarFile = fs.inputFile(fs.predicates().is(context.getFile())); CommentLinesVisitor commentLinesVisitor = createCommentLineVisitorAndFindNoSonar(context); if(isSonarLintContext()) { // No need to compute metrics on SonarLint side, but the no sonar filter is still required return; } classTrees.clear(); methods = 0; complexityInMethods = 0; classes = 0; methodComplexityDistribution = new RangeDistributionBuilder(LIMITS_COMPLEXITY_METHODS); super.setContext(context); scanTree(context.getTree()); //leave file. int fileComplexity = context.getComplexityNodes(context.getTree()).size(); saveMetricOnFile(CoreMetrics.CLASSES, classes); saveMetricOnFile(CoreMetrics.FUNCTIONS, methods); saveMetricOnFile(CoreMetrics.COMPLEXITY_IN_FUNCTIONS, complexityInMethods); saveMetricOnFile(CoreMetrics.COMPLEXITY_IN_CLASSES, fileComplexity); saveMetricOnFile(CoreMetrics.COMPLEXITY, fileComplexity); saveMetricOnFile(CoreMetrics.COMMENT_LINES, commentLinesVisitor.commentLinesMetric()); saveMetricOnFile(CoreMetrics.STATEMENTS, new StatementVisitor().numberOfStatements(context.getTree())); saveMetricOnFile(CoreMetrics.NCLOC, new LinesOfCodeVisitor().linesOfCode(context.getTree())); saveMetricOnFile(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION, methodComplexityDistribution.build()); RangeDistributionBuilder fileComplexityDistribution = new RangeDistributionBuilder(LIMITS_COMPLEXITY_FILES); saveMetricOnFile(CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION, fileComplexityDistribution.add(fileComplexity).build()); saveMetricOnFile(CoreMetrics.COGNITIVE_COMPLEXITY, CognitiveComplexityVisitor.compilationUnitComplexity(context.getTree())); }
@Override public void visitConditionalExpression(ConditionalExpressionTree tree) { increaseComplexityByNesting(tree.questionToken()); nesting++; super.visitConditionalExpression(tree); nesting--; }