aboutsummaryrefslogtreecommitdiff
path: root/clang-tidy/bugprone
diff options
context:
space:
mode:
authorAlexander Kornienko <alexfh@google.com>2018-02-28 23:30:29 +0000
committerAlexander Kornienko <alexfh@google.com>2018-02-28 23:30:29 +0000
commit2f873bc9c0ea4804c3efc64144e1a24e2720647e (patch)
tree2b3a27c591c687b8a7b15193ad7d71397ac784f1 /clang-tidy/bugprone
parentab3c36c546847ac325260062a903e3bdeb4e620f (diff)
Rename more checks from misc- to bugprone-.
Summary: clang-tidy/rename_check.py {misc,bugprone}-string-integer-assignment clang-tidy/rename_check.py {misc,bugprone}-string-literal-with-embedded-nul clang-tidy/rename_check.py {misc,bugprone}-suspicious-enum-usage clang-tidy/rename_check.py {misc,bugprone}-suspicious-missing-comma Reviewers: hokein, sammccall, aaron.ballman Subscribers: klimek, cfe-commits, mgorny Differential Revision: https://reviews.llvm.org/D43868 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@326384 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'clang-tidy/bugprone')
-rw-r--r--clang-tidy/bugprone/BugproneTidyModule.cpp12
-rw-r--r--clang-tidy/bugprone/CMakeLists.txt4
-rw-r--r--clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp86
-rw-r--r--clang-tidy/bugprone/StringIntegerAssignmentCheck.h35
-rw-r--r--clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.cpp85
-rw-r--r--clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.h35
-rw-r--r--clang-tidy/bugprone/SuspiciousEnumUsageCheck.cpp219
-rw-r--r--clang-tidy/bugprone/SuspiciousEnumUsageCheck.h39
-rw-r--r--clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp129
-rw-r--r--clang-tidy/bugprone/SuspiciousMissingCommaCheck.h44
10 files changed, 688 insertions, 0 deletions
diff --git a/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tidy/bugprone/BugproneTidyModule.cpp
index 5e67672f..8ce22721 100644
--- a/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -28,7 +28,11 @@
#include "MoveForwardingReferenceCheck.h"
#include "MultipleStatementMacroCheck.h"
#include "StringConstructorCheck.h"
+#include "StringIntegerAssignmentCheck.h"
+#include "StringLiteralWithEmbeddedNulCheck.h"
+#include "SuspiciousEnumUsageCheck.h"
#include "SuspiciousMemsetUsageCheck.h"
+#include "SuspiciousMissingCommaCheck.h"
#include "ThrowKeywordMissingCheck.h"
#include "UndefinedMemoryManipulationCheck.h"
#include "UseAfterMoveCheck.h"
@@ -77,8 +81,16 @@ public:
"bugprone-multiple-statement-macro");
CheckFactories.registerCheck<StringConstructorCheck>(
"bugprone-string-constructor");
+ CheckFactories.registerCheck<StringIntegerAssignmentCheck>(
+ "bugprone-string-integer-assignment");
+ CheckFactories.registerCheck<StringLiteralWithEmbeddedNulCheck>(
+ "bugprone-string-literal-with-embedded-nul");
+ CheckFactories.registerCheck<SuspiciousEnumUsageCheck>(
+ "bugprone-suspicious-enum-usage");
CheckFactories.registerCheck<SuspiciousMemsetUsageCheck>(
"bugprone-suspicious-memset-usage");
+ CheckFactories.registerCheck<SuspiciousMissingCommaCheck>(
+ "bugprone-suspicious-missing-comma");
CheckFactories.registerCheck<ThrowKeywordMissingCheck>(
"bugprone-throw-keyword-missing");
CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
diff --git a/clang-tidy/bugprone/CMakeLists.txt b/clang-tidy/bugprone/CMakeLists.txt
index 73b4e55f..42e7b4fd 100644
--- a/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tidy/bugprone/CMakeLists.txt
@@ -20,7 +20,11 @@ add_clang_library(clangTidyBugproneModule
MoveForwardingReferenceCheck.cpp
MultipleStatementMacroCheck.cpp
StringConstructorCheck.cpp
+ StringIntegerAssignmentCheck.cpp
+ StringLiteralWithEmbeddedNulCheck.cpp
+ SuspiciousEnumUsageCheck.cpp
SuspiciousMemsetUsageCheck.cpp
+ SuspiciousMissingCommaCheck.cpp
ThrowKeywordMissingCheck.cpp
UndefinedMemoryManipulationCheck.cpp
UseAfterMoveCheck.cpp
diff --git a/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp b/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp
new file mode 100644
index 00000000..f3736489
--- /dev/null
+++ b/clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp
@@ -0,0 +1,86 @@
+//===--- StringIntegerAssignmentCheck.cpp - clang-tidy---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StringIntegerAssignmentCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+void StringIntegerAssignmentCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+ Finder->addMatcher(
+ cxxOperatorCallExpr(
+ anyOf(hasOverloadedOperatorName("="),
+ hasOverloadedOperatorName("+=")),
+ callee(cxxMethodDecl(ofClass(classTemplateSpecializationDecl(
+ hasName("::std::basic_string"),
+ hasTemplateArgument(0, refersToType(qualType().bind("type"))))))),
+ hasArgument(1,
+ ignoringImpCasts(expr(hasType(isInteger()),
+ unless(hasType(isAnyCharacter())))
+ .bind("expr"))),
+ unless(isInTemplateInstantiation())),
+ this);
+}
+
+void StringIntegerAssignmentCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Argument = Result.Nodes.getNodeAs<Expr>("expr");
+ SourceLocation Loc = Argument->getLocStart();
+
+ auto Diag =
+ diag(Loc, "an integer is interpreted as a character code when assigning "
+ "it to a string; if this is intended, cast the integer to the "
+ "appropriate character type; if you want a string "
+ "representation, use the appropriate conversion facility");
+
+ if (Loc.isMacroID())
+ return;
+
+ auto CharType = *Result.Nodes.getNodeAs<QualType>("type");
+ bool IsWideCharType = CharType->isWideCharType();
+ if (!CharType->isCharType() && !IsWideCharType)
+ return;
+ bool IsOneDigit = false;
+ bool IsLiteral = false;
+ if (const auto *Literal = dyn_cast<IntegerLiteral>(Argument)) {
+ IsOneDigit = Literal->getValue().getLimitedValue() < 10;
+ IsLiteral = true;
+ }
+
+ SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ Argument->getLocEnd(), 0, *Result.SourceManager, getLangOpts());
+ if (IsOneDigit) {
+ Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L'" : "'")
+ << FixItHint::CreateInsertion(EndLoc, "'");
+ return;
+ }
+ if (IsLiteral) {
+ Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L\"" : "\"")
+ << FixItHint::CreateInsertion(EndLoc, "\"");
+ return;
+ }
+
+ if (getLangOpts().CPlusPlus11) {
+ Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "std::to_wstring("
+ : "std::to_string(")
+ << FixItHint::CreateInsertion(EndLoc, ")");
+ }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/bugprone/StringIntegerAssignmentCheck.h b/clang-tidy/bugprone/StringIntegerAssignmentCheck.h
new file mode 100644
index 00000000..42fa53e9
--- /dev/null
+++ b/clang-tidy/bugprone/StringIntegerAssignmentCheck.h
@@ -0,0 +1,35 @@
+//===--- StringIntegerAssignmentCheck.h - clang-tidy-------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_STRINGINTEGERASSIGNMENTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_STRINGINTEGERASSIGNMENTCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds instances where an integer is assigned to a string.
+///
+/// For more details see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-string-assignment.html
+class StringIntegerAssignmentCheck : public ClangTidyCheck {
+public:
+ StringIntegerAssignmentCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_STRINGINTEGERASSIGNMENTCHECK_H
diff --git a/clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.cpp b/clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.cpp
new file mode 100644
index 00000000..eaa610fc
--- /dev/null
+++ b/clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.cpp
@@ -0,0 +1,85 @@
+//===--- StringLiteralWithEmbeddedNulCheck.cpp - clang-tidy----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StringLiteralWithEmbeddedNulCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+namespace {
+AST_MATCHER(StringLiteral, containsNul) {
+ for (size_t i = 0; i < Node.getLength(); ++i)
+ if (Node.getCodeUnit(i) == '\0')
+ return true;
+ return false;
+}
+} // namespace
+
+void StringLiteralWithEmbeddedNulCheck::registerMatchers(MatchFinder *Finder) {
+ // Match a string that contains embedded NUL character. Extra-checks are
+ // applied in |check| to find incorectly escaped characters.
+ Finder->addMatcher(stringLiteral(containsNul()).bind("strlit"), this);
+
+ // The remaining checks only apply to C++.
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ const auto StrLitWithNul =
+ ignoringParenImpCasts(stringLiteral(containsNul()).bind("truncated"));
+
+ // Match string constructor.
+ const auto StringConstructorExpr = expr(anyOf(
+ cxxConstructExpr(argumentCountIs(1),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string")))),
+ // If present, the second argument is the alloc object which must not
+ // be present explicitly.
+ cxxConstructExpr(argumentCountIs(2),
+ hasDeclaration(cxxMethodDecl(hasName("basic_string"))),
+ hasArgument(1, cxxDefaultArgExpr()))));
+
+ // Detect passing a suspicious string literal to a string constructor.
+ // example: std::string str = "abc\0def";
+ Finder->addMatcher(
+ cxxConstructExpr(StringConstructorExpr, hasArgument(0, StrLitWithNul)),
+ this);
+
+ // Detect passing a suspicious string literal through an overloaded operator.
+ Finder->addMatcher(cxxOperatorCallExpr(hasAnyArgument(StrLitWithNul)), this);
+}
+
+void StringLiteralWithEmbeddedNulCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *SL = Result.Nodes.getNodeAs<StringLiteral>("strlit")) {
+ for (size_t Offset = 0, Length = SL->getLength(); Offset < Length;
+ ++Offset) {
+ // Find a sequence of character like "\0x12".
+ if (Offset + 3 < Length && SL->getCodeUnit(Offset) == '\0' &&
+ SL->getCodeUnit(Offset + 1) == 'x' &&
+ isDigit(SL->getCodeUnit(Offset + 2)) &&
+ isDigit(SL->getCodeUnit(Offset + 3))) {
+ diag(SL->getLocStart(), "suspicious embedded NUL character");
+ return;
+ }
+ }
+ }
+
+ if (const auto *SL = Result.Nodes.getNodeAs<StringLiteral>("truncated")) {
+ diag(SL->getLocStart(),
+ "truncated string literal with embedded NUL character");
+ }
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.h b/clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.h
new file mode 100644
index 00000000..f5341c31
--- /dev/null
+++ b/clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.h
@@ -0,0 +1,35 @@
+//===--- StringLiteralWithEmbeddedNulCheck.h - clang-tidy--------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_STRINGLITERALWITHEMBEDDEDNULCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_STRINGLITERALWITHEMBEDDEDNULCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Find suspicious string literals with embedded NUL characters.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-string-literal-with-embedded-nul.html
+class StringLiteralWithEmbeddedNulCheck : public ClangTidyCheck {
+public:
+ StringLiteralWithEmbeddedNulCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_STRINGLITERALWITHEMBEDDEDNULCHECK_H
diff --git a/clang-tidy/bugprone/SuspiciousEnumUsageCheck.cpp b/clang-tidy/bugprone/SuspiciousEnumUsageCheck.cpp
new file mode 100644
index 00000000..1abad4eb
--- /dev/null
+++ b/clang-tidy/bugprone/SuspiciousEnumUsageCheck.cpp
@@ -0,0 +1,219 @@
+//===--- SuspiciousEnumUsageCheck.cpp - clang-tidy-------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SuspiciousEnumUsageCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <algorithm>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+static const char DifferentEnumErrorMessage[] =
+ "enum values are from different enum types";
+
+static const char BitmaskErrorMessage[] =
+ "enum type seems like a bitmask (contains mostly "
+ "power-of-2 literals), but this literal is not a "
+ "power-of-2";
+
+static const char BitmaskVarErrorMessage[] =
+ "enum type seems like a bitmask (contains mostly "
+ "power-of-2 literals) but %plural{1:a literal is|:some literals are}0 not "
+ "power-of-2";
+
+static const char BitmaskNoteMessage[] = "used here as a bitmask";
+
+/// Stores a min and a max value which describe an interval.
+struct ValueRange {
+ llvm::APSInt MinVal;
+ llvm::APSInt MaxVal;
+
+ ValueRange(const EnumDecl *EnumDec) {
+ const auto MinMaxVal = std::minmax_element(
+ EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
+ [](const EnumConstantDecl *E1, const EnumConstantDecl *E2) {
+ return llvm::APSInt::compareValues(E1->getInitVal(),
+ E2->getInitVal()) < 0;
+ });
+ MinVal = MinMaxVal.first->getInitVal();
+ MaxVal = MinMaxVal.second->getInitVal();
+ }
+};
+
+/// Return the number of EnumConstantDecls in an EnumDecl.
+static int enumLength(const EnumDecl *EnumDec) {
+ return std::distance(EnumDec->enumerator_begin(), EnumDec->enumerator_end());
+}
+
+static bool hasDisjointValueRange(const EnumDecl *Enum1,
+ const EnumDecl *Enum2) {
+ ValueRange Range1(Enum1), Range2(Enum2);
+ return llvm::APSInt::compareValues(Range1.MaxVal, Range2.MinVal) < 0 ||
+ llvm::APSInt::compareValues(Range2.MaxVal, Range1.MinVal) < 0;
+}
+
+static bool isNonPowerOf2NorNullLiteral(const EnumConstantDecl *EnumConst) {
+ llvm::APSInt Val = EnumConst->getInitVal();
+ if (Val.isPowerOf2() || !Val.getBoolValue())
+ return false;
+ const Expr *InitExpr = EnumConst->getInitExpr();
+ if (!InitExpr)
+ return true;
+ return isa<IntegerLiteral>(InitExpr->IgnoreImpCasts());
+}
+
+static bool isMaxValAllBitSetLiteral(const EnumDecl *EnumDec) {
+ auto EnumConst = std::max_element(
+ EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
+ [](const EnumConstantDecl *E1, const EnumConstantDecl *E2) {
+ return E1->getInitVal() < E2->getInitVal();
+ });
+
+ if (const Expr *InitExpr = EnumConst->getInitExpr()) {
+ return EnumConst->getInitVal().countTrailingOnes() ==
+ EnumConst->getInitVal().getActiveBits() &&
+ isa<IntegerLiteral>(InitExpr->IgnoreImpCasts());
+ }
+ return false;
+}
+
+static int countNonPowOfTwoLiteralNum(const EnumDecl *EnumDec) {
+ return std::count_if(
+ EnumDec->enumerator_begin(), EnumDec->enumerator_end(),
+ [](const EnumConstantDecl *E) { return isNonPowerOf2NorNullLiteral(E); });
+}
+
+/// Check if there is one or two enumerators that are not a power of 2 and are
+/// initialized by a literal in the enum type, and that the enumeration contains
+/// enough elements to reasonably act as a bitmask. Exclude the case where the
+/// last enumerator is the sum of the lesser values (and initialized by a
+/// literal) or when it could contain consecutive values.
+static bool isPossiblyBitMask(const EnumDecl *EnumDec) {
+ ValueRange VR(EnumDec);
+ int EnumLen = enumLength(EnumDec);
+ int NonPowOfTwoCounter = countNonPowOfTwoLiteralNum(EnumDec);
+ return NonPowOfTwoCounter >= 1 && NonPowOfTwoCounter <= 2 &&
+ NonPowOfTwoCounter < EnumLen / 2 &&
+ (VR.MaxVal - VR.MinVal != EnumLen - 1) &&
+ !(NonPowOfTwoCounter == 1 && isMaxValAllBitSetLiteral(EnumDec));
+}
+
+SuspiciousEnumUsageCheck::SuspiciousEnumUsageCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ StrictMode(Options.getLocalOrGlobal("StrictMode", 0)) {}
+
+void SuspiciousEnumUsageCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "StrictMode", StrictMode);
+}
+
+void SuspiciousEnumUsageCheck::registerMatchers(MatchFinder *Finder) {
+ const auto enumExpr = [](StringRef RefName, StringRef DeclName) {
+ return allOf(ignoringImpCasts(expr().bind(RefName)),
+ ignoringImpCasts(hasType(enumDecl().bind(DeclName))));
+ };
+
+ Finder->addMatcher(
+ binaryOperator(hasOperatorName("|"), hasLHS(enumExpr("", "enumDecl")),
+ hasRHS(allOf(enumExpr("", "otherEnumDecl"),
+ ignoringImpCasts(hasType(enumDecl(
+ unless(equalsBoundNode("enumDecl"))))))))
+ .bind("diffEnumOp"),
+ this);
+
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|")),
+ hasLHS(enumExpr("lhsExpr", "enumDecl")),
+ hasRHS(allOf(enumExpr("rhsExpr", ""),
+ ignoringImpCasts(hasType(enumDecl(
+ equalsBoundNode("enumDecl"))))))),
+ this);
+
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("|")),
+ hasEitherOperand(
+ allOf(hasType(isInteger()), unless(enumExpr("", "")))),
+ hasEitherOperand(enumExpr("enumExpr", "enumDecl"))),
+ this);
+
+ Finder->addMatcher(
+ binaryOperator(anyOf(hasOperatorName("|="), hasOperatorName("+=")),
+ hasRHS(enumExpr("enumExpr", "enumDecl"))),
+ this);
+}
+
+void SuspiciousEnumUsageCheck::checkSuspiciousBitmaskUsage(
+ const Expr *NodeExpr, const EnumDecl *EnumDec) {
+ const auto *EnumExpr = dyn_cast<DeclRefExpr>(NodeExpr);
+ const auto *EnumConst =
+ EnumExpr ? dyn_cast<EnumConstantDecl>(EnumExpr->getDecl()) : nullptr;
+
+ // Report the parameter if neccessary.
+ if (!EnumConst) {
+ diag(EnumDec->getInnerLocStart(), BitmaskVarErrorMessage)
+ << countNonPowOfTwoLiteralNum(EnumDec);
+ diag(EnumExpr->getExprLoc(), BitmaskNoteMessage, DiagnosticIDs::Note);
+ } else if (isNonPowerOf2NorNullLiteral(EnumConst)) {
+ diag(EnumConst->getSourceRange().getBegin(), BitmaskErrorMessage);
+ diag(EnumExpr->getExprLoc(), BitmaskNoteMessage, DiagnosticIDs::Note);
+ }
+}
+
+void SuspiciousEnumUsageCheck::check(const MatchFinder::MatchResult &Result) {
+ // Case 1: The two enum values come from different types.
+ if (const auto *DiffEnumOp =
+ Result.Nodes.getNodeAs<BinaryOperator>("diffEnumOp")) {
+ const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>("enumDecl");
+ const auto *OtherEnumDec =
+ Result.Nodes.getNodeAs<EnumDecl>("otherEnumDecl");
+ // Skip when one of the parameters is an empty enum. The
+ // hasDisjointValueRange function could not decide the values properly in
+ // case of an empty enum.
+ if (EnumDec->enumerator_begin() == EnumDec->enumerator_end() ||
+ OtherEnumDec->enumerator_begin() == OtherEnumDec->enumerator_end())
+ return;
+
+ if (!hasDisjointValueRange(EnumDec, OtherEnumDec))
+ diag(DiffEnumOp->getOperatorLoc(), DifferentEnumErrorMessage);
+ return;
+ }
+
+ // Case 2 and 3 only checked in strict mode. The checker tries to detect
+ // suspicious bitmasks which contains values initialized by non power-of-2
+ // literals.
+ if (!StrictMode)
+ return;
+ const auto *EnumDec = Result.Nodes.getNodeAs<EnumDecl>("enumDecl");
+ if (!isPossiblyBitMask(EnumDec))
+ return;
+
+ // Case 2:
+ // a. Investigating the right hand side of `+=` or `|=` operator.
+ // b. When the operator is `|` or `+` but only one of them is an EnumExpr
+ if (const auto *EnumExpr = Result.Nodes.getNodeAs<Expr>("enumExpr")) {
+ checkSuspiciousBitmaskUsage(EnumExpr, EnumDec);
+ return;
+ }
+
+ // Case 3:
+ // '|' or '+' operator where both argument comes from the same enum type
+ const auto *LhsExpr = Result.Nodes.getNodeAs<Expr>("lhsExpr");
+ checkSuspiciousBitmaskUsage(LhsExpr, EnumDec);
+
+ const auto *RhsExpr = Result.Nodes.getNodeAs<Expr>("rhsExpr");
+ checkSuspiciousBitmaskUsage(RhsExpr, EnumDec);
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/bugprone/SuspiciousEnumUsageCheck.h b/clang-tidy/bugprone/SuspiciousEnumUsageCheck.h
new file mode 100644
index 00000000..9c1b53d7
--- /dev/null
+++ b/clang-tidy/bugprone/SuspiciousEnumUsageCheck.h
@@ -0,0 +1,39 @@
+//===--- SuspiciousEnumUsageCheck.h - clang-tidy--------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSENUMUSAGECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSENUMUSAGECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// The checker detects various cases when an enum is probably misused (as a
+/// bitmask).
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-enum-usage.html
+class SuspiciousEnumUsageCheck : public ClangTidyCheck {
+public:
+ SuspiciousEnumUsageCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+ void checkSuspiciousBitmaskUsage(const Expr*, const EnumDecl*);
+ const bool StrictMode;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSENUMUSAGECHECK_H
diff --git a/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp b/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp
new file mode 100644
index 00000000..3831dc3e
--- /dev/null
+++ b/clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp
@@ -0,0 +1,129 @@
+//===--- SuspiciousMissingCommaCheck.cpp - clang-tidy----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SuspiciousMissingCommaCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+namespace {
+
+bool isConcatenatedLiteralsOnPurpose(ASTContext *Ctx,
+ const StringLiteral *Lit) {
+ // String literals surrounded by parentheses are assumed to be on purpose.
+ // i.e.: const char* Array[] = { ("a" "b" "c"), "d", [...] };
+ auto Parents = Ctx->getParents(*Lit);
+ if (Parents.size() == 1 && Parents[0].get<ParenExpr>() != nullptr)
+ return true;
+
+ // Appropriately indented string literals are assumed to be on purpose.
+ // The following frequent indentation is accepted:
+ // const char* Array[] = {
+ // "first literal"
+ // "indented literal"
+ // "indented literal",
+ // "second literal",
+ // [...]
+ // };
+ const SourceManager &SM = Ctx->getSourceManager();
+ bool IndentedCorrectly = true;
+ SourceLocation FirstToken = Lit->getStrTokenLoc(0);
+ FileID BaseFID = SM.getFileID(FirstToken);
+ unsigned int BaseIndent = SM.getSpellingColumnNumber(FirstToken);
+ unsigned int BaseLine = SM.getSpellingLineNumber(FirstToken);
+ for (unsigned int TokNum = 1; TokNum < Lit->getNumConcatenated(); ++TokNum) {
+ SourceLocation Token = Lit->getStrTokenLoc(TokNum);
+ FileID FID = SM.getFileID(Token);
+ unsigned int Indent = SM.getSpellingColumnNumber(Token);
+ unsigned int Line = SM.getSpellingLineNumber(Token);
+ if (FID != BaseFID || Line != BaseLine + TokNum || Indent <= BaseIndent) {
+ IndentedCorrectly = false;
+ break;
+ }
+ }
+ if (IndentedCorrectly)
+ return true;
+
+ // There is no pattern recognized by the checker, assume it's not on purpose.
+ return false;
+}
+
+AST_MATCHER_P(StringLiteral, isConcatenatedLiteral, unsigned,
+ MaxConcatenatedTokens) {
+ return Node.getNumConcatenated() > 1 &&
+ Node.getNumConcatenated() < MaxConcatenatedTokens &&
+ !isConcatenatedLiteralsOnPurpose(&Finder->getASTContext(), &Node);
+}
+
+} // namespace
+
+SuspiciousMissingCommaCheck::SuspiciousMissingCommaCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ SizeThreshold(Options.get("SizeThreshold", 5U)),
+ RatioThreshold(std::stod(Options.get("RatioThreshold", ".2"))),
+ MaxConcatenatedTokens(Options.get("MaxConcatenatedTokens", 5U)) {}
+
+void SuspiciousMissingCommaCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "SizeThreshold", SizeThreshold);
+ Options.store(Opts, "RatioThreshold", std::to_string(RatioThreshold));
+ Options.store(Opts, "MaxConcatenatedTokens", MaxConcatenatedTokens);
+}
+
+void SuspiciousMissingCommaCheck::registerMatchers(MatchFinder *Finder) {
+ const auto ConcatenatedStringLiteral =
+ stringLiteral(isConcatenatedLiteral(MaxConcatenatedTokens)).bind("str");
+
+ const auto StringsInitializerList =
+ initListExpr(hasType(constantArrayType()),
+ has(ignoringParenImpCasts(expr(ConcatenatedStringLiteral))));
+
+ Finder->addMatcher(StringsInitializerList.bind("list"), this);
+}
+
+void SuspiciousMissingCommaCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *InitializerList = Result.Nodes.getNodeAs<InitListExpr>("list");
+ const auto *ConcatenatedLiteral =
+ Result.Nodes.getNodeAs<StringLiteral>("str");
+ assert(InitializerList && ConcatenatedLiteral);
+
+ // Skip small arrays as they often generate false-positive.
+ unsigned int Size = InitializerList->getNumInits();
+ if (Size < SizeThreshold)
+ return;
+
+ // Count the number of occurence of concatenated string literal.
+ unsigned int Count = 0;
+ for (unsigned int i = 0; i < Size; ++i) {
+ const Expr *Child = InitializerList->getInit(i)->IgnoreImpCasts();
+ if (const auto *Literal = dyn_cast<StringLiteral>(Child)) {
+ if (Literal->getNumConcatenated() > 1)
+ ++Count;
+ }
+ }
+
+ // Warn only when concatenation is not common in this initializer list.
+ // The current threshold is set to less than 1/5 of the string literals.
+ if (double(Count) / Size > RatioThreshold)
+ return;
+
+ diag(ConcatenatedLiteral->getLocStart(),
+ "suspicious string literal, probably missing a comma");
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tidy/bugprone/SuspiciousMissingCommaCheck.h b/clang-tidy/bugprone/SuspiciousMissingCommaCheck.h
new file mode 100644
index 00000000..4ae3ecf0
--- /dev/null
+++ b/clang-tidy/bugprone/SuspiciousMissingCommaCheck.h
@@ -0,0 +1,44 @@
+//===--- SuspiciousMissingCommaCheck.h - clang-tidy--------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSMISSINGCOMMACHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSMISSINGCOMMACHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// This check finds string literals which are probably concatenated
+/// accidentally.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-suspicious-missing-comma.html
+class SuspiciousMissingCommaCheck : public ClangTidyCheck {
+public:
+ SuspiciousMissingCommaCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ // Minimal size of a string literals array to be considered by the checker.
+ const unsigned SizeThreshold;
+ // Maximal threshold ratio of suspicious string literals to be considered.
+ const double RatioThreshold;
+ // Maximal number of concatenated tokens.
+ const unsigned MaxConcatenatedTokens;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SUSPICIOUSMISSINGCOMMACHECK_H