Skip to content
Draft
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 @@ -356,14 +356,21 @@ public void doTest() {
.that(result)
.isEqualTo(expectedResult.orElse(Result.OK));
} else {
ImmutableList.Builder<String> mismatches = ImmutableList.builder();
for (JavaFileObject source : sources) {
try {
diagnosticHelper.assertHasDiagnosticOnAllMatchingLines(
source, lookForCheckNameInDiagnostic);
for (String mismatch :
diagnosticHelper.findMismatchedLines(source, lookForCheckNameInDiagnostic)) {
mismatches.add(sources.size() > 1 ? source.getName() + ": " + mismatch : mismatch);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
ImmutableList<String> allMismatches = mismatches.build();
if (!allMismatches.isEmpty()) {
fail(diagnosticHelper.describeMismatches(allMismatches));
}
assertWithMessage("Unused error keys: %s", diagnosticHelper.getUnusedLookupKeys())
.that(diagnosticHelper.getUnusedLookupKeys().isEmpty())
.isTrue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.google.errorprone;

import static com.google.common.truth.Truth.assertWithMessage;
import static java.util.Locale.ENGLISH;
import static org.hamcrest.Matchers.hasItem;
import static org.junit.Assert.fail;
Expand Down Expand Up @@ -216,23 +215,44 @@ public void expectErrorMessage(String key, Predicate<? super String> matcher) {
/**
* Asserts that the diagnostics contain a diagnostic on each line of the source file that matches
* our bug marker pattern. Parses the bug marker pattern for the specific string to look for in
* the diagnostic.
* the diagnostic. The failure message names every line that fails, each with the first
* expectation it fails.
*
* @param source File in which to find matching lines
*/
public void assertHasDiagnosticOnAllMatchingLines(
JavaFileObject source, LookForCheckNameInDiagnostic lookForCheckNameInDiagnostic)
throws IOException {
ImmutableList<String> mismatches = findMismatchedLines(source, lookForCheckNameInDiagnostic);
if (!mismatches.isEmpty()) {
fail(describeMismatches(mismatches));
}
}

/**
* Returns one message for each line of {@code source} that fails, in source order, naming the
* first expectation the line fails: a marker whose key has no expected message, a marked line
* without a matching diagnostic, or an unmarked line with a diagnostic. The message for a marked
* line without a matching diagnostic is followed by the text of that line, indented, since the
* list of all errors prints the source line only for a line that has a diagnostic.
*/
ImmutableList<String> findMismatchedLines(
JavaFileObject source, LookForCheckNameInDiagnostic lookForCheckNameInDiagnostic)
throws IOException {
List<Diagnostic<? extends JavaFileObject>> diagnostics = getDiagnostics();
LineNumberReader reader =
new LineNumberReader(CharSource.wrap(source.getCharContent(false)).openStream());
ImmutableList.Builder<String> mismatches = ImmutableList.builder();
CharSequence content = source.getCharContent(false);
List<String> sourceLines = content.toString().lines().toList();
LineNumberReader reader = new LineNumberReader(CharSource.wrap(content).openStream());
do {
String line = reader.readLine();
if (line == null) {
break;
}

List<Predicate<? super String>> predicates = null;
// The first expectation this line fails, so that each line gets at most one message.
String mismatch = null;
if (line.contains(BUG_MARKER_COMMENT_INLINE)) {
// Diagnostic must contain all patterns from the bug marker comment.
List<String> patterns = extractPatterns(line, reader, BUG_MARKER_COMMENT_INLINE);
Expand All @@ -245,12 +265,14 @@ public void assertHasDiagnosticOnAllMatchingLines(
List<String> lookupKeys = extractPatterns(line, reader, BUG_MARKER_COMMENT_LOOKUP);
predicates = new ArrayList<>(lookupKeys.size());
for (String lookupKey : lookupKeys) {
assertWithMessage(
"No expected error message with key [%s] as expected from line [%s] "
+ "with diagnostic [%s]",
lookupKey, markerLineNumber, line.trim())
.that(expectedErrorMsgs.containsKey(lookupKey))
.isTrue();
if (!expectedErrorMsgs.containsKey(lookupKey)) {
mismatch =
String.format(
"No expected error message with key [%s] as expected from line [%s] "
+ "with diagnostic [%s]",
lookupKey, markerLineNumber, line.trim());
break;
}
predicates.add(expectedErrorMsgs.get(lookupKey));
usedLookupKeys.add(lookupKey);
}
Expand All @@ -261,36 +283,52 @@ public void assertHasDiagnosticOnAllMatchingLines(
for (Predicate<? super String> predicate : predicates) {
Matcher<? super Iterable<Diagnostic<? extends JavaFileObject>>> patternMatcher =
hasItem(diagnosticOnLine(source.toUri(), lineNumber, predicate));
assertWithMessage(
"Did not see an error on line %s matching %s. %s",
lineNumber, predicate, allErrors(diagnostics))
.that(patternMatcher.matches(diagnostics))
.isTrue();
if (mismatch == null && !patternMatcher.matches(diagnostics)) {
mismatch =
String.format(
"Did not see an error on line %s matching %s.\n %s",
lineNumber, predicate, sourceLines.get(lineNumber - 1).trim());
}
}

if (checkName != null && lookForCheckNameInDiagnostic == LookForCheckNameInDiagnostic.YES) {
if (mismatch == null
&& checkName != null
&& lookForCheckNameInDiagnostic == LookForCheckNameInDiagnostic.YES) {
// Diagnostic must contain check name.
Matcher<? super Iterable<Diagnostic<? extends JavaFileObject>>> checkNameMatcher =
hasItem(
diagnosticOnLine(
source.toUri(), lineNumber, new SimpleStringContains("[" + checkName + "]")));
assertWithMessage(
"Did not see an error on line %s containing [%s]. %s",
lineNumber, checkName, allErrors(diagnostics))
.that(checkNameMatcher.matches(diagnostics))
.isTrue();
if (!checkNameMatcher.matches(diagnostics)) {
mismatch =
String.format(
"Did not see an error on line %s containing [%s].\n %s",
lineNumber, checkName, sourceLines.get(lineNumber - 1).trim());
}
}

} else {
int lineNumber = reader.getLineNumber();
Matcher<? super Iterable<Diagnostic<? extends JavaFileObject>>> matcher =
hasItem(diagnosticOnLine(source.toUri(), lineNumber));
if (matcher.matches(diagnostics)) {
fail("Saw unexpected error on line " + lineNumber + ". " + allErrors(diagnostics));
mismatch = "Saw unexpected error on line " + lineNumber + ".";
}
}
if (mismatch != null) {
mismatches.add(mismatch);
}
} while (true);
reader.close();
return mismatches.build();
}

/**
* Returns a failure message with each of {@code mismatches} on its own line, followed by every
* diagnostic the compilation reported.
*/
String describeMismatches(List<String> mismatches) {
return String.join("\n", mismatches) + "\n" + allErrors(getDiagnostics());
}

private static String allErrors(List<Diagnostic<? extends JavaFileObject>> diagnostics) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,165 @@ public boolean doIt() {
assertThat(expected).hasMessageThat().contains("Did not see an error on line 3");
}

@Test
public void fileWithTwoBugMarkersAndNoErrorsReportsBothLines() {
AssertionError expected =
assertThrows(
AssertionError.class,
() ->
compilationHelper
.addSourceLines(
"Test.java",
"""
public class Test {
// BUG: Diagnostic contains:
public void doIt() {}

// BUG: Diagnostic contains:
public void doItAgain() {}
}
""")
.doTest());
assertThat(expected)
.hasMessageThat()
.isEqualTo(
"""
Did not see an error on line 3 matching .
public void doIt() {}
Did not see an error on line 6 matching .
public void doItAgain() {}
There were no errors.""");
}

@Test
public void fileWithTwoUnexpectedErrorsReportsBothLinesBeforeTheDiagnostics() {
AssertionError expected =
assertThrows(
AssertionError.class,
() ->
compilationHelper
.addSourceLines(
"Test.java",
"""
public class Test {
public boolean doIt() {
return true;
}

public boolean doItAgain() {
return false;
}
}
""")
.doTest());
assertThat(expected)
.hasMessageThat()
.startsWith(
"""
Saw unexpected error on line 3.
Saw unexpected error on line 7.
All errors:
""");
}

@Test
public void markerWithUnknownKeyIsReportedWithTheOtherMismatches() {
AssertionError expected =
assertThrows(
AssertionError.class,
() ->
compilationHelper
.addSourceLines(
"Test.java",
"""
public class Test {
public boolean doIt() {
return true;
}

// BUG: Diagnostic matches: X
public void doItAgain() {}
}
""")
.doTest());
assertThat(expected)
.hasMessageThat()
.startsWith(
"""
Saw unexpected error on line 3.
No expected error message with key [X] as expected from line [6] \
with diagnostic [// BUG: Diagnostic matches: X]
All errors:
""");
}

@Test
public void fileWithUnexpectedErrorBeforeMissingOneReportsBothLines() {
AssertionError expected =
assertThrows(
AssertionError.class,
() ->
compilationHelper
.addSourceLines(
"Test.java",
"""
public class Test {
public boolean doIt() {
return true;
}

// BUG: Diagnostic contains:
public void doItAgain() {}
}
""")
.doTest());
assertThat(expected)
.hasMessageThat()
.startsWith(
"""
Saw unexpected error on line 3.
Did not see an error on line 7 matching .
public void doItAgain() {}
All errors:
""");
}

@Test
public void mismatchesInTwoFilesAreReportedWithTheirFileNames() {
AssertionError expected =
assertThrows(
AssertionError.class,
() ->
compilationHelper
.addSourceLines(
"A.java",
"""
public class A {
public boolean doIt() {
return true;
}
}
""")
.addSourceLines(
"B.java",
"""
public class B {
// BUG: Diagnostic contains:
public void doIt() {}
}
""")
.doTest());
assertThat(expected)
.hasMessageThat()
.startsWith(
"""
/A.java: Saw unexpected error on line 3.
/B.java: Did not see an error on line 3 matching .
public void doIt() {}
All errors:
""");
}

@Test
public void fileWithMultipleBugMarkersAndMatchingErrorsSucceeds() {
compilationHelper
Expand Down Expand Up @@ -272,6 +431,14 @@ public void fileWithSyntaxErrorFails() {
" return}",
"}")
.doTest());
assertThat(expected)
.hasMessageThat()
.startsWith(
"""
Did not see an error on line 4 containing [ReturnTreeChecker].
return}
All errors:
""");
assertThat(expected).hasMessageThat().contains("error: illegal start of expression");
}

Expand Down
Loading