alexey.knyshev created this revision.
alexey.knyshev added a project: clang.
Herald added subscribers: cfe-commits, mgorny.

Implementation of checker "different.LabelInsideSwitch" from potential checkers 
list (https://clang-analyzer.llvm.org/potential_checkers.html#different)


Repository:
  rC Clang

https://reviews.llvm.org/D40715

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  lib/StaticAnalyzer/Checkers/CMakeLists.txt
  lib/StaticAnalyzer/Checkers/LabelInsideSwitchChecker.cpp
  test/Analysis/label-inside-switch.c

Index: test/Analysis/label-inside-switch.c
===================================================================
--- test/Analysis/label-inside-switch.c
+++ test/Analysis/label-inside-switch.c
@@ -0,0 +1,57 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core.LabelInsideSwitch -w -verify %s
+
+int caseAnddefaultMissprint(unsigned count) {
+  int res = 0;
+  switch(count) {
+  case 1:
+    res = 1;
+    break;
+  cas: // expected-warning {{label inside switch (did you mean 'case'?)}}
+  case 2:
+    res = 5;
+    break;
+  case 3:
+    exit(1);
+  defaul: // expected-warning {{label inside switch (did you mean 'default'?)}}
+    return -1;
+  }
+
+  return res;
+}
+
+int nestedSwitch(unsigned x, unsigned y) {
+  int res = 0;
+  switch (x) {
+  case 1:
+    res = 1;
+    break;
+  case 2:
+    res = 5;
+    break;
+  case 3:
+    switch(y) {
+    case 1:
+    case 2:
+    case 4:
+      res = x * y;
+      break;
+    defaul:; // expected-warning {{label inside switch (did you mean 'default'?)}}
+    }
+    break;
+  default:;
+  }
+
+  return res;
+}
+
+int arbitaryLabelInSwitch(int arg) {
+  switch (arg)  {
+  case 1:
+  case 2:
+  label: // expected-warning {{label inside switch}}
+  case 3:
+    break;
+  }
+
+  return 0;
+}
Index: lib/StaticAnalyzer/Checkers/LabelInsideSwitchChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/LabelInsideSwitchChecker.cpp
+++ lib/StaticAnalyzer/Checkers/LabelInsideSwitchChecker.cpp
@@ -0,0 +1,121 @@
+#include "clang/AST/StmtVisitor.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+  class LabelInsideSwitchChecker : public Checker< check::ASTCodeBody > {
+  public:
+    void checkASTCodeBody(const Decl *D, AnalysisManager &mgr, BugReporter &BR) const;
+  };
+
+  static std::pair<StringRef, bool> Suggest(const StringRef &Str, StringRef Exp);
+  static std::pair<StringRef, bool> SuggestTypoFix(const StringRef &Str) {
+    const auto S = Suggest(Str, "default");
+    if (S.second)
+      return S;
+
+    return Suggest(Str, "case");
+  }
+
+  class WalkAST : public ConstStmtVisitor<WalkAST, void, bool> {
+    const CheckerBase *Checker;
+    BugReporter &BR;
+    AnalysisDeclContext *AC;
+
+  public:
+    WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC)
+        : Checker(Checker), BR(BR), AC(AC) {}
+
+    // Statement visitor methods.
+    void VisitChildren(const Stmt *S, bool InSwitch) {
+      for (const Stmt *Child : S->children())
+        if (Child)
+          Visit(Child, InSwitch);
+    }
+
+    void VisitStmt(const Stmt *S, bool InSwitch) {
+      if (!InSwitch && isa<SwitchStmt>(S)) {
+        InSwitch = true;
+      } else if (InSwitch && isa<LabelStmt>(S)) {
+        const LabelStmt *L = cast<LabelStmt>(S);
+        const auto Sug = SuggestTypoFix(L->getName());
+
+        SmallString<256> SBuf;
+        llvm::raw_svector_ostream OS(SBuf);
+        OS << "label inside switch";
+        if (Sug.second) {
+          OS << " (did you mean '" << Sug.first << "'?)";
+        }
+
+        PathDiagnosticLocation Loc =
+          PathDiagnosticLocation::createBegin(S, BR.getSourceManager(), AC);
+        SourceRange Sr[1];
+        Sr[0] = S->getSourceRange();
+        BR.EmitBasicReport(AC->getDecl(), Checker, "Labeled statement inside switch",
+                           categories::LogicError, OS.str(), Loc, Sr);
+      }
+      VisitChildren(S, InSwitch);
+    }
+  };
+
+  std::pair<StringRef, bool> Suggest(const StringRef &Str, StringRef Exp) {
+    const size_t StrSize = Str.size();
+    const size_t ExpSize = Exp.size();
+
+    assert(StrSize);
+    assert(ExpSize);
+
+    size_t SizeDelta = 0;
+    size_t MinSize = StrSize;
+
+    if (StrSize < ExpSize) {
+      SizeDelta = ExpSize - StrSize;
+    } else if (StrSize > ExpSize) {
+      SizeDelta = StrSize - ExpSize;
+      MinSize = ExpSize;
+    }
+
+    // We only accept size delta <= 1
+    if (SizeDelta > 1)
+        return std::make_pair("", false);
+
+    size_t SameSymCount = 0;
+    for (size_t i = 0; i < MinSize; i++)
+      if (Str[i] == Exp[i])
+        SameSymCount++;
+
+    if (SameSymCount > 0 && SameSymCount - SizeDelta >= ExpSize / 2)
+      return std::make_pair(Exp, true);
+
+    // Str & Exp have the same size. No sence to check from back to front
+    if (SizeDelta == 0)
+      return std::make_pair("", false);
+
+    SameSymCount = 0;
+    for (size_t i = StrSize, j = ExpSize; i > 0 && j > 0; i--, j--)
+    if (Str[i - 1] == Exp[j - 1])
+      SameSymCount++;
+
+    if (SameSymCount > 0 && SameSymCount - SizeDelta >= ExpSize / 2)
+      return std::make_pair(Exp, true);
+
+    return std::make_pair("", false);
+  }
+} // end anonymous namespace
+
+void LabelInsideSwitchChecker::checkASTCodeBody(const Decl *D, AnalysisManager &mgr, BugReporter &BR) const {
+  WalkAST walker(this, BR, mgr.getAnalysisDeclContext(D));
+  walker.Visit(D->getBody(), /*InSwitch=*/ false);
+}
+
+namespace clang {
+  namespace ento {
+    void registerLabelInsideSwitchChecker(CheckerManager &mgr) {
+      mgr.registerChecker<LabelInsideSwitchChecker>();
+    }
+  } // end ento namespace
+} // end clang namespace
Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -43,6 +43,7 @@
   IteratorChecker.cpp
   IvarInvalidationChecker.cpp
   LLVMConventionsChecker.cpp
+  LabelInsideSwitchChecker.cpp
   LocalizationChecker.cpp
   MacOSKeychainAPIChecker.cpp
   MacOSXAPIChecker.cpp
Index: include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -160,6 +160,10 @@
   HelpText<"Warn about unintended use of identical expressions in operators">,
   DescFile<"IdenticalExprChecker.cpp">;
 
+def LabelInsideSwitchChecker : Checker<"LabelInsideSwitch">,
+  HelpText<"Warn about labeled statements inside switch">,
+  DescFile<"LabelInsideSwitchChecker.cpp">;
+
 def FixedAddressChecker : Checker<"FixedAddr">,
   HelpText<"Check for assignment of a fixed address to a pointer">,
   DescFile<"FixedAddressChecker.cpp">;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to