Skip to content

Commit

Permalink
Add global analyzer to catch common mistakes
Browse files Browse the repository at this point in the history
  • Loading branch information
sanderploegsma committed Jan 19, 2024
1 parent c7c64e1 commit 42f0f67
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 7 deletions.
3 changes: 3 additions & 0 deletions src/main/java/analyzer/AnalyzerRoot.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package analyzer;

import analyzer.exercises.GlobalAnalyzer;
import analyzer.exercises.hamming.HammingAnalyzer;
import analyzer.exercises.twofer.TwoferAnalyzer;
import com.github.javaparser.ast.CompilationUnit;
Expand All @@ -25,6 +26,8 @@ private static List<Analyzer> createAnalyzers(String slug) {
case "two-fer" -> analyzers.add(new TwoferAnalyzer());
}

analyzers.add(new GlobalAnalyzer());

return List.copyOf(analyzers);
}
}
19 changes: 19 additions & 0 deletions src/main/java/analyzer/comments/AvoidPrintStatements.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package analyzer.comments;

import analyzer.Comment;
import analyzer.CommentType;

/**
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/general/avoid_print_statements.md">Markdown Template</a>
*/
public class AvoidPrintStatements extends Comment {
@Override
public String getKey() {
return "java.general.avoid_print_statements";
}

@Override
public CommentType getType() {
return CommentType.INFORMATIVE;
}
}
19 changes: 19 additions & 0 deletions src/main/java/analyzer/comments/DoNotUseMainMethod.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package analyzer.comments;

import analyzer.Comment;
import analyzer.CommentType;

/**
* @see <a href="https://github.com/exercism/website-copy/blob/main/analyzer-comments/java/general/do_not_use_main_method.md">Markdown Template</a>
*/
public class DoNotUseMainMethod extends Comment {
@Override
public String getKey() {
return "java.general.do_not_use_main_method";
}

@Override
public CommentType getType() {
return CommentType.ESSENTIAL;
}
}
52 changes: 52 additions & 0 deletions src/main/java/analyzer/exercises/GlobalAnalyzer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package analyzer.exercises;

import analyzer.Analysis;
import analyzer.Analyzer;
import analyzer.comments.AvoidPrintStatements;
import analyzer.comments.DoNotUseMainMethod;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;

import java.util.List;

public class GlobalAnalyzer extends VoidVisitorAdapter<Void> implements Analyzer {
private Analysis analysis;

@Override
public void analyze(List<CompilationUnit> compilationUnits, Analysis analysis) {
this.analysis = analysis;
for (CompilationUnit compilationUnit : compilationUnits) {
compilationUnit.accept(this, null);
}
}

@Override
public void visit(MethodDeclaration n, Void arg) {
if (isMainMethod(n)) {
analysis.addComment(new DoNotUseMainMethod());
}

super.visit(n, arg);
}

@Override
public void visit(MethodCallExpr n, Void arg) {
if (isPrintStatement(n)) {
analysis.addComment(new AvoidPrintStatements());
}

super.visit(n, arg);
}

private static boolean isMainMethod(MethodDeclaration node) {
return node.getNameAsString().equals("main") && node.isStatic() && node.getType().isVoidType();
}

private static boolean isPrintStatement(MethodCallExpr node) {
return node.getScope()
.map(scope -> scope.toString().matches("System\\.(?:out|err)"))
.orElse(false);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package analyzer;

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;

import java.util.List;

public abstract class ExerciseAnalyzerTest {
public abstract class AnalyzerTest {
protected abstract Analyzer getAnalyzer();

protected Analysis analyzeResourceFile(String resourceFileName) {
var resource = getClass().getResourceAsStream(resourceFileName);
var compilationUnit = StaticJavaParser.parse(resource);
return analyze(StaticJavaParser.parse(resource));
}

protected Analysis analyzeString(String javaCode) {
return analyze(StaticJavaParser.parse(javaCode));
}

private Analysis analyze(CompilationUnit compilationUnit) {
var analysis = new Analysis();
getAnalyzer().analyze(List.of(compilationUnit), analysis);
return analysis;
Expand Down
79 changes: 79 additions & 0 deletions src/test/java/analyzer/exercises/GlobalAnalyzerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package analyzer.exercises;

import analyzer.Analyzer;
import analyzer.AnalyzerTest;
import analyzer.comments.AvoidPrintStatements;
import analyzer.comments.DoNotUseMainMethod;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;

public class GlobalAnalyzerTest extends AnalyzerTest {
@Override
protected Analyzer getAnalyzer() {
return new GlobalAnalyzer();
}

@MethodSource
@ParameterizedTest
public void solutionsWithMainMethod(String solution) {
var actual = analyzeString(solution);
assertThat(actual.getComments()).contains(new DoNotUseMainMethod());
}

private static Stream<String> solutionsWithMainMethod() {
return Stream.of(
"""
class Solution {
public static void main(String... args) {}
}""",
"""
class Solution {
public static void main(String ...args) {}
}""",
"""
class Solution {
public static void main(String[] args) {}
}"""
);
}

@MethodSource
@ParameterizedTest
public void solutionsWithPrintStatements(String solution) {
var actual = analyzeString(solution);
assertThat(actual.getComments()).contains(new AvoidPrintStatements());
}

private static Stream<String> solutionsWithPrintStatements() {
return Stream.of(
"""
class Solution {
void method() {
System.out.println("Printing line to stdout");
}
}""",
"""
class Solution {
void method() {
System.err.println("Printing line to stderr");
}
}""",
"""
class Solution {
void method() {
System.out.print("Printing to stdout");
}
}""",
"""
class Solution {
void method() {
System.err.print("Printing to stderr");
}
}"""
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import analyzer.Analyzer;
import analyzer.Comment;
import analyzer.ExerciseAnalyzerTest;
import analyzer.AnalyzerTest;
import analyzer.comments.ConstructorTooLong;
import analyzer.comments.MethodTooLong;
import analyzer.comments.UseProperClassName;
Expand All @@ -15,7 +15,7 @@

import static org.assertj.core.api.Assertions.assertThat;

public class HammingAnalyzerTest extends ExerciseAnalyzerTest {
public class HammingAnalyzerTest extends AnalyzerTest {
@Override
protected Analyzer getAnalyzer() {
return new HammingAnalyzer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import analyzer.Analyzer;
import analyzer.Comment;
import analyzer.ExerciseAnalyzerTest;
import analyzer.AnalyzerTest;
import analyzer.comments.AvoidHardCodedTestCases;
import analyzer.comments.UseProperClassName;
import analyzer.comments.UseProperMethodName;
Expand All @@ -14,7 +14,7 @@

import static org.assertj.core.api.Assertions.assertThat;

public class TwoferAnalyzerTest extends ExerciseAnalyzerTest {
public class TwoferAnalyzerTest extends AnalyzerTest {

@Override
protected Analyzer getAnalyzer() {
Expand Down
11 changes: 10 additions & 1 deletion tests/unknown-exercise/expected_analysis.json
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
{}
{"comments": [
{
"comment": "java.general.do_not_use_main_method",
"type": "essential"
},
{
"comment": "java.general.avoid_print_statements",
"type": "informative"
}
]}
5 changes: 5 additions & 0 deletions tests/unknown-exercise/src/main/java/UnknownExercise.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@ class UnknownExercise {
int calculate() {
return 42;
}

public static void main(String[] args) {
var exercise = new UnknownExercise();
System.out.println(exercise.calculate());
}
}

0 comments on commit 42f0f67

Please sign in to comment.