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">; 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: 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 Suggest(const StringRef &Str, StringRef Exp); + static std::pair SuggestTypoFix(const StringRef &Str) { + const auto S = Suggest(Str, "default"); + if (S.second) + return S; + + return Suggest(Str, "case"); + } + + class WalkAST : public ConstStmtVisitor { + 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(S)) { + InSwitch = true; + } else if (InSwitch && isa(S)) { + const LabelStmt *L = cast(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 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(); + } + } // end ento namespace +} // end clang namespace 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; +}