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,104 @@ +//== LabelInsideSwitchChecker.cpp - Lable inside switch checker -*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines LabelInsideSwitchChecker, which looks for typos in switch +// cases like missprinting 'defualt', 'cas' or other accidental insertion +// of labeled statement. +// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { +class LabelInsideSwitchChecker : public Checker { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const; +}; + +static StringRef suggestTypoFix(StringRef Str) { + StringRef DefaultStr("default"); + const unsigned DefaultStrED = + Str.edit_distance(DefaultStr, true, DefaultStr.size() / 2); + + if (DefaultStrED <= DefaultStr.size() / 2) + return DefaultStr; + + StringRef CaseStr("case"); + const unsigned CaseStrED = + Str.edit_distance(CaseStr, true, CaseStr.size() / 2); + + if (CaseStrED <= CaseStr.size() / 2) + return CaseStr; + + return StringRef(); +} + +class Callback : public MatchFinder::MatchCallback { + const CheckerBase *C; + BugReporter &BR; + AnalysisDeclContext *AC; + + void report(const LabelStmt *L) { + StringRef Suggest = suggestTypoFix(L->getName()); + + SmallString<64> Message; + llvm::raw_svector_ostream OS(Message); + OS << "label inside switch"; + if (!Suggest.empty()) + OS << " (did you mean '" << Suggest << "'?)"; + + PathDiagnosticLocation Loc = + PathDiagnosticLocation::createBegin(L, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), C, "Labeled statement inside switch", + categories::LogicError, Message, Loc, + L->getSourceRange()); + } + +public: + Callback(const CheckerBase *C, BugReporter &BR, AnalysisDeclContext *AC) + : C(C), BR(BR), AC(AC) {} + + virtual void run(const MatchFinder::MatchResult &Result) override { + if (const LabelStmt *L = Result.Nodes.getNodeAs("label")) + report(L); + if (const LabelStmt *L = Result.Nodes.getNodeAs("label_in_case")) + report(L); + } +}; +} // end anonymous namespace + +void LabelInsideSwitchChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &Mgr, + BugReporter &BR) const { + auto LabelStmt = stmt(hasDescendant(switchStmt( + eachOf(has(compoundStmt(forEach(labelStmt().bind("label")))), + forEachSwitchCase(forEach(labelStmt().bind("label_in_case"))))))); + + MatchFinder F; + Callback CB(this, BR, Mgr.getAnalysisDeclContext(D)); + + F.addMatcher(LabelStmt, &CB); + F.match(*D->getBody(), Mgr.getASTContext()); +} + +namespace clang { +namespace ento { +void registerLabelInsideSwitchChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} +} // namespace ento +} // namespace clang Index: test/Analysis/label-inside-switch.c =================================================================== --- test/Analysis/label-inside-switch.c +++ test/Analysis/label-inside-switch.c @@ -0,0 +1,67 @@ +// 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; +} + +int unreachableLabelInSwitch(int arg) { + switch (arg) { + unreachable: // expected-warning {{label inside switch}} + case 1: + return 0; + default: + return arg; + } +}