/*
 * Copyright 2017 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.bugpatterns;

import static com.google.common.collect.Iterables.getLast;
import static com.google.errorprone.BugPattern.Category.JDK;
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static com.google.errorprone.matchers.Description.NO_MATCH;

import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker.SwitchTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.Reachability;
import com.sun.source.tree.SwitchTree;
import com.sun.tools.javac.tree.JCTree;
import java.util.regex.Pattern;

/** @author cushon@google.com (Liam Miller-Cushon) */
@BugPattern(
  name = "FallThrough",
  altNames = "fallthrough",
  category = JDK,
  summary = "Switch case may fall through",
  severity = ERROR
)
public class FallThrough extends BugChecker implements SwitchTreeMatcher {

  // TODO(cushon): be more prescriptive about using "fall through"
  private static final Pattern FALL_THROUGH_PATTERN =
      Pattern.compile("fall.*through|break|continue|fallthru", Pattern.CASE_INSENSITIVE);

  private static final Pattern PRECISE_FALL_THROUGH_PATTERN =
      Pattern.compile("fall.*through", Pattern.CASE_INSENSITIVE);

  @Override
  public Description matchSwitch(SwitchTree tree, VisitorState state) {
    PeekingIterator<JCTree.JCCase> it =
        Iterators.peekingIterator(((JCTree.JCSwitch) tree).cases.iterator());
    while (it.hasNext()) {
      JCTree.JCCase caseTree = it.next();
      if (!it.hasNext()) {
        break;
      }
      JCTree.JCCase next = it.peek();
      if (caseTree.stats.isEmpty()) {
        continue;
      }
      // We only care whether the last statement completes; javac would have already
      // reported an error if that statement wasn't reachable, and the answer is
      // independent of any preceding statements.
      boolean completes = Reachability.canCompleteNormally(getLast(caseTree.stats));
      String comments =
          state
              .getSourceCode()
              .subSequence(state.getEndPosition(caseTree), next.getStartPosition())
              .toString()
              .trim();
      if (completes && !FALL_THROUGH_PATTERN.matcher(comments).find()) {
        state.reportMatch(describeMatch(next, SuggestedFix.prefixWith(next, "// fall through\n")));
      } else if (!completes && PRECISE_FALL_THROUGH_PATTERN.matcher(comments).find()) {
        SuggestedFix deleteFallThroughComment =
            SuggestedFix.replace(state.getEndPosition(caseTree), next.getStartPosition(), "\n");
        state.reportMatch(
            buildDescription(next)
                .addFix(deleteFallThroughComment)
                .setMessage("Switch case has 'fall through' comment, but does not fall through")
                .build());
      }
    }
    return NO_MATCH;
  }
}

@@ -1701,75 +1701,8 @@
  %7B%0A%0A
-  // TODO(cushon): be more prescriptive about using %22fall through%22%0A
   pr
@@ -1778,174 +1778,29 @@
 le(%22
-fall.*through%7Cbreak%7Ccontinue%7Cfallthru%22, Pattern.CASE_INSENSITIVE);%0A%0A  private static final Pattern PRECISE_FALL_THROUGH_PATTERN =%0A      Pattern.compile(%22fall.*
+%5C%5Cbfalls?.?
 through
+%5C%5Cb
 %22, P
@@ -2962,16 +2962,8 @@
  && 
-PRECISE_
 FALL

Make "fall through" pattern more precise