/* * Copyright 2011 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.errorprone; import static com.google.common.base.Preconditions.checkNotNull; import com.google.errorprone.fixes.AppliedFix; import com.google.errorprone.fixes.Fix; import com.google.errorprone.matchers.Description; import com.sun.tools.javac.tree.EndPosTable; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.Log; import java.io.IOError; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; import javax.tools.JavaFileObject; /** * Making our errors appear to the user and break their build. * @author alexeagle@google.com (Alex Eagle) */ public class JavacErrorDescriptionListener implements DescriptionListener { private final Log log; private final JavaFileObject sourceFile; private final Function<Fix, AppliedFix> fixToAppliedFix; // When we're trying to refactor using error prone fixes, any error halts compilation of other // files. We set this to true when refactoring so we can log every hit without breaking the // compile. private final boolean dontUseErrors; // The suffix for properties in src/main/resources/com/google/errorprone/errors.properties private static final String MESSAGE_BUNDLE_KEY = "error.prone"; private JavacErrorDescriptionListener( Log log, EndPosTable endPositions, JavaFileObject sourceFile, boolean dontUseErrors) { this.log = log; this.sourceFile = sourceFile; this.dontUseErrors = dontUseErrors; checkNotNull(endPositions); try { CharSequence sourceFileContent = sourceFile.getCharContent(true); fixToAppliedFix = fix -> AppliedFix.fromSource(sourceFileContent, endPositions).apply(fix); } catch (IOException e) { throw new IOError(e); } } @Override public void onDescribed(Description description) { List<AppliedFix> appliedFixes = description .fixes .stream() .map(fixToAppliedFix) .filter(Objects::nonNull) .collect(Collectors.toCollection(ArrayList::new)); String message = messageForFixes(description, appliedFixes); // Swap the log's source and the current file's source; then be sure to swap them back later. JavaFileObject originalSource = log.useSource(sourceFile); switch (description.severity) { case ERROR: if (dontUseErrors) { log.warning((DiagnosticPosition) description.node, MESSAGE_BUNDLE_KEY, message); } else { log.error((DiagnosticPosition) description.node, MESSAGE_BUNDLE_KEY, message); } break; case WARNING: log.warning((DiagnosticPosition) description.node, MESSAGE_BUNDLE_KEY, message); break; case SUGGESTION: log.note((DiagnosticPosition) description.node, MESSAGE_BUNDLE_KEY, message); break; default: break; } if (originalSource != null) { log.useSource(originalSource); } } private static String messageForFixes(Description description, List<AppliedFix> appliedFixes) { StringBuilder messageBuilder = new StringBuilder(description.getMessage()); boolean first = true; for (AppliedFix appliedFix : appliedFixes) { if (first) { messageBuilder.append("\nDid you mean "); } else { messageBuilder.append(" or "); } if (appliedFix.isRemoveLine()) { messageBuilder.append("to remove this line"); } else { messageBuilder.append("'").append(appliedFix.getNewCodeSnippet()).append("'"); } first = false; } if (!first) { // appended at least one suggested fix to the message messageBuilder.append("?"); } return messageBuilder.toString(); } static Factory provider() { return (log, compilation) -> new JavacErrorDescriptionListener( log, compilation.endPositions, compilation.getSourceFile(), false); } static Factory providerForRefactoring() { return (log, compilation) -> new JavacErrorDescriptionListener( log, compilation.endPositions, compilation.getSourceFile(), true); } }