Index: clang-tidy/bugprone/BugproneTidyModule.cpp =================================================================== --- clang-tidy/bugprone/BugproneTidyModule.cpp +++ 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 @@ "bugprone-multiple-statement-macro"); CheckFactories.registerCheck( "bugprone-string-constructor"); + CheckFactories.registerCheck( + "bugprone-string-integer-assignment"); + CheckFactories.registerCheck( + "bugprone-string-literal-with-embedded-nul"); + CheckFactories.registerCheck( + "bugprone-suspicious-enum-usage"); CheckFactories.registerCheck( "bugprone-suspicious-memset-usage"); + CheckFactories.registerCheck( + "bugprone-suspicious-missing-comma"); CheckFactories.registerCheck( "bugprone-throw-keyword-missing"); CheckFactories.registerCheck( Index: clang-tidy/bugprone/CMakeLists.txt =================================================================== --- clang-tidy/bugprone/CMakeLists.txt +++ clang-tidy/bugprone/CMakeLists.txt @@ -20,7 +20,11 @@ MoveForwardingReferenceCheck.cpp MultipleStatementMacroCheck.cpp StringConstructorCheck.cpp + StringIntegerAssignmentCheck.cpp + StringLiteralWithEmbeddedNulCheck.cpp + SuspiciousEnumUsageCheck.cpp SuspiciousMemsetUsageCheck.cpp + SuspiciousMissingCommaCheck.cpp ThrowKeywordMissingCheck.cpp UndefinedMemoryManipulationCheck.cpp UseAfterMoveCheck.cpp Index: clang-tidy/bugprone/StringIntegerAssignmentCheck.h =================================================================== --- clang-tidy/bugprone/StringIntegerAssignmentCheck.h +++ 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 Index: clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp =================================================================== --- clang-tidy/bugprone/StringIntegerAssignmentCheck.cpp +++ 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"); + 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("type"); + bool IsWideCharType = CharType->isWideCharType(); + if (!CharType->isCharType() && !IsWideCharType) + return; + bool IsOneDigit = false; + bool IsLiteral = false; + if (const auto *Literal = dyn_cast(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 Index: clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.h =================================================================== --- clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.h +++ 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 Index: clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.cpp =================================================================== --- clang-tidy/bugprone/StringLiteralWithEmbeddedNulCheck.cpp +++ 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("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("truncated")) { + diag(SL->getLocStart(), + "truncated string literal with embedded NUL character"); + } +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang Index: clang-tidy/bugprone/SuspiciousEnumUsageCheck.h =================================================================== --- clang-tidy/bugprone/SuspiciousEnumUsageCheck.h +++ 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 Index: clang-tidy/bugprone/SuspiciousEnumUsageCheck.cpp =================================================================== --- clang-tidy/bugprone/SuspiciousEnumUsageCheck.cpp +++ 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 + +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(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(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(NodeExpr); + const auto *EnumConst = + EnumExpr ? dyn_cast(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("diffEnumOp")) { + const auto *EnumDec = Result.Nodes.getNodeAs("enumDecl"); + const auto *OtherEnumDec = + Result.Nodes.getNodeAs("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"); + 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("enumExpr")) { + checkSuspiciousBitmaskUsage(EnumExpr, EnumDec); + return; + } + + // Case 3: + // '|' or '+' operator where both argument comes from the same enum type + const auto *LhsExpr = Result.Nodes.getNodeAs("lhsExpr"); + checkSuspiciousBitmaskUsage(LhsExpr, EnumDec); + + const auto *RhsExpr = Result.Nodes.getNodeAs("rhsExpr"); + checkSuspiciousBitmaskUsage(RhsExpr, EnumDec); +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang Index: clang-tidy/bugprone/SuspiciousMissingCommaCheck.h =================================================================== --- clang-tidy/bugprone/SuspiciousMissingCommaCheck.h +++ 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 Index: clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp =================================================================== --- clang-tidy/bugprone/SuspiciousMissingCommaCheck.cpp +++ 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() != 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("list"); + const auto *ConcatenatedLiteral = + Result.Nodes.getNodeAs("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(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 Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -12,10 +12,6 @@ SizeofContainerCheck.cpp SizeofExpressionCheck.cpp StaticAssertCheck.cpp - StringIntegerAssignmentCheck.cpp - StringLiteralWithEmbeddedNulCheck.cpp - SuspiciousEnumUsageCheck.cpp - SuspiciousMissingCommaCheck.cpp SuspiciousSemicolonCheck.cpp SuspiciousStringCompareCheck.cpp SwappedArgumentsCheck.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -19,10 +19,6 @@ #include "SizeofContainerCheck.h" #include "SizeofExpressionCheck.h" #include "StaticAssertCheck.h" -#include "StringIntegerAssignmentCheck.h" -#include "StringLiteralWithEmbeddedNulCheck.h" -#include "SuspiciousEnumUsageCheck.h" -#include "SuspiciousMissingCommaCheck.h" #include "SuspiciousSemicolonCheck.h" #include "SuspiciousStringCompareCheck.h" #include "SwappedArgumentsCheck.h" @@ -59,14 +55,6 @@ CheckFactories.registerCheck( "misc-sizeof-expression"); CheckFactories.registerCheck("misc-static-assert"); - CheckFactories.registerCheck( - "misc-string-integer-assignment"); - CheckFactories.registerCheck( - "misc-string-literal-with-embedded-nul"); - CheckFactories.registerCheck( - "misc-suspicious-enum-usage"); - CheckFactories.registerCheck( - "misc-suspicious-missing-comma"); CheckFactories.registerCheck( "misc-suspicious-semicolon"); CheckFactories.registerCheck( Index: clang-tidy/misc/StringIntegerAssignmentCheck.h =================================================================== --- clang-tidy/misc/StringIntegerAssignmentCheck.h +++ clang-tidy/misc/StringIntegerAssignmentCheck.h @@ -1,35 +0,0 @@ -//===--- 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_MISC_STRING_INTEGER_ASSIGNMENT_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// 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 misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H Index: clang-tidy/misc/StringIntegerAssignmentCheck.cpp =================================================================== --- clang-tidy/misc/StringIntegerAssignmentCheck.cpp +++ clang-tidy/misc/StringIntegerAssignmentCheck.cpp @@ -1,86 +0,0 @@ -//===--- 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 misc { - -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"); - 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("type"); - bool IsWideCharType = CharType->isWideCharType(); - if (!CharType->isCharType() && !IsWideCharType) - return; - bool IsOneDigit = false; - bool IsLiteral = false; - if (const auto *Literal = dyn_cast(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 misc -} // namespace tidy -} // namespace clang Index: clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.h =================================================================== --- clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.h +++ clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.h @@ -1,35 +0,0 @@ -//===--- 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_MISC_STRING_LITERAL_WITH_EMBEDDED_NUL_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_LITERAL_WITH_EMBEDDED_NUL_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// Find suspicious string literals with embedded NUL characters. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/misc-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 misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_LITERAL_WITH_EMBEDDED_NUL_H Index: clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.cpp =================================================================== --- clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.cpp +++ clang-tidy/misc/StringLiteralWithEmbeddedNulCheck.cpp @@ -1,85 +0,0 @@ -//===--- 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 misc { - -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("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("truncated")) { - diag(SL->getLocStart(), - "truncated string literal with embedded NUL character"); - } -} - -} // namespace misc -} // namespace tidy -} // namespace clang Index: clang-tidy/misc/SuspiciousEnumUsageCheck.h =================================================================== --- clang-tidy/misc/SuspiciousEnumUsageCheck.h +++ clang-tidy/misc/SuspiciousEnumUsageCheck.h @@ -1,39 +0,0 @@ -//===--- 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_MISC_SUSPICIOUS_ENUM_USAGE_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_ENUM_USAGE_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// 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/misc-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 misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_ENUM_USAGE_H Index: clang-tidy/misc/SuspiciousEnumUsageCheck.cpp =================================================================== --- clang-tidy/misc/SuspiciousEnumUsageCheck.cpp +++ clang-tidy/misc/SuspiciousEnumUsageCheck.cpp @@ -1,219 +0,0 @@ -//===--- 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 - -using namespace clang::ast_matchers; - -namespace clang { -namespace tidy { -namespace misc { - -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(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(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(NodeExpr); - const auto *EnumConst = - EnumExpr ? dyn_cast(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("diffEnumOp")) { - const auto *EnumDec = Result.Nodes.getNodeAs("enumDecl"); - const auto *OtherEnumDec = - Result.Nodes.getNodeAs("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"); - 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("enumExpr")) { - checkSuspiciousBitmaskUsage(EnumExpr, EnumDec); - return; - } - - // Case 3: - // '|' or '+' operator where both argument comes from the same enum type - const auto *LhsExpr = Result.Nodes.getNodeAs("lhsExpr"); - checkSuspiciousBitmaskUsage(LhsExpr, EnumDec); - - const auto *RhsExpr = Result.Nodes.getNodeAs("rhsExpr"); - checkSuspiciousBitmaskUsage(RhsExpr, EnumDec); -} - -} // namespace misc -} // namespace tidy -} // namespace clang Index: clang-tidy/misc/SuspiciousMissingCommaCheck.h =================================================================== --- clang-tidy/misc/SuspiciousMissingCommaCheck.h +++ clang-tidy/misc/SuspiciousMissingCommaCheck.h @@ -1,44 +0,0 @@ -//===--- 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_MISC_SUSPICIOUS_MISSING_COMMA_H -#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_MISSING_COMMA_H - -#include "../ClangTidy.h" - -namespace clang { -namespace tidy { -namespace misc { - -/// This check finds string literals which are probably concatenated -/// accidentally. -/// -/// For the user-facing documentation see: -/// http://clang.llvm.org/extra/clang-tidy/checks/misc-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 misc -} // namespace tidy -} // namespace clang - -#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_MISSING_COMMA_H Index: clang-tidy/misc/SuspiciousMissingCommaCheck.cpp =================================================================== --- clang-tidy/misc/SuspiciousMissingCommaCheck.cpp +++ clang-tidy/misc/SuspiciousMissingCommaCheck.cpp @@ -1,129 +0,0 @@ -//===--- 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 misc { - -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() != 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("list"); - const auto *ConcatenatedLiteral = - Result.Nodes.getNodeAs("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(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 misc -} // namespace tidy -} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -100,9 +100,9 @@ Warns if SIMD intrinsics are used which can be replaced by ``std::experimental::simd`` operations. - + - New alias `hicpp-avoid-goto - `_ to + `_ to `cppcoreguidelines-avoid-goto `_ added. @@ -124,6 +124,18 @@ - The 'misc-string-compare' check was renamed to `readability-string-compare `_ +- The 'misc-string-integer-assignment' check was renamed to `bugprone-string-integer-assignment + `_ + +- The 'misc-string-literal-with-embedded-nul' check was renamed to `bugprone-string-literal-with-embedded-nul + `_ + +- The 'misc-suspicious-enum-usage' check was renamed to `bugprone-suspicious-enum-usage + `_ + +- The 'misc-suspicious-missing-comma' check was renamed to `bugprone-suspicious-missing-comma + `_ + Improvements to include-fixer ----------------------------- Index: docs/clang-tidy/checks/bugprone-string-integer-assignment.rst =================================================================== --- docs/clang-tidy/checks/bugprone-string-integer-assignment.rst +++ docs/clang-tidy/checks/bugprone-string-integer-assignment.rst @@ -0,0 +1,37 @@ +.. title:: clang-tidy - bugprone-string-integer-assignment + +bugprone-string-integer-assignment +================================== + +The check finds assignments of an integer to ``std::basic_string`` +(``std::string``, ``std::wstring``, etc.). The source of the problem is the +following assignment operator of ``std::basic_string``: + +.. code-block:: c++ + + basic_string& operator=( CharT ch ); + +Numeric types can be implicitly casted to character types. + +.. code-block:: c++ + + std::string s; + int x = 5965; + s = 6; + s = x; + +Use the appropriate conversion functions or character literals. + +.. code-block:: c++ + + std::string s; + int x = 5965; + s = '6'; + s = std::to_string(x); + +In order to suppress false positives, use an explicit cast. + +.. code-block:: c++ + + std::string s; + s = static_cast(6); Index: docs/clang-tidy/checks/bugprone-string-literal-with-embedded-nul.rst =================================================================== --- docs/clang-tidy/checks/bugprone-string-literal-with-embedded-nul.rst +++ docs/clang-tidy/checks/bugprone-string-literal-with-embedded-nul.rst @@ -0,0 +1,36 @@ +.. title:: clang-tidy - bugprone-string-literal-with-embedded-nul + +bugprone-string-literal-with-embedded-nul +========================================= + +Finds occurrences of string literal with embedded NUL character and validates +their usage. + +Invalid escaping +---------------- + +Special characters can be escaped within a string literal by using their +hexadecimal encoding like ``\x42``. A common mistake is to escape them +like this ``\0x42`` where the ``\0`` stands for the NUL character. + +.. code-block:: c++ + + const char* Example[] = "Invalid character: \0x12 should be \x12"; + const char* Bytes[] = "\x03\0x02\0x01\0x00\0xFF\0xFF\0xFF"; + +Truncated literal +----------------- + +String-like classes can manipulate strings with embedded NUL as they are keeping +track of the bytes and the length. This is not the case for a ``char*`` +(NUL-terminated) string. + +A common mistake is to pass a string-literal with embedded NUL to a string +constructor expecting a NUL-terminated string. The bytes after the first NUL +character are truncated. + +.. code-block:: c++ + + std::string str("abc\0def"); // "def" is truncated + str += "\0"; // This statement is doing nothing + if (str == "\0abc") return; // This expression is always true Index: docs/clang-tidy/checks/bugprone-suspicious-enum-usage.rst =================================================================== --- docs/clang-tidy/checks/bugprone-suspicious-enum-usage.rst +++ docs/clang-tidy/checks/bugprone-suspicious-enum-usage.rst @@ -0,0 +1,78 @@ +.. title:: clang-tidy - bugprone-suspicious-enum-usage + +bugprone-suspicious-enum-usage +============================== + +The checker detects various cases when an enum is probably misused (as a bitmask +). + +1. When "ADD" or "bitwise OR" is used between two enum which come from different + types and these types value ranges are not disjoint. + +The following cases will be investigated only using :option:`StrictMode`. We +regard the enum as a (suspicious) +bitmask if the three conditions below are true at the same time: + +* at most half of the elements of the enum are non pow-of-2 numbers (because of + short enumerations) +* there is another non pow-of-2 number than the enum constant representing all + choices (the result "bitwise OR" operation of all enum elements) +* enum type variable/enumconstant is used as an argument of a `+` or "bitwise OR + " operator + +So whenever the non pow-of-2 element is used as a bitmask element we diagnose a +misuse and give a warning. + +2. Investigating the right hand side of `+=` and `|=` operator. +3. Check only the enum value side of a `|` and `+` operator if one of them is not + enum val. +4. Check both side of `|` or `+` operator where the enum values are from the + same enum type. + +Examples: + +.. code-block:: c++ + + enum { A, B, C }; + enum { D, E, F = 5 }; + enum { G = 10, H = 11, I = 12 }; + + unsigned flag; + flag = + A | + H; // OK, disjoint value intervalls in the enum types ->probably good use. + flag = B | F; // Warning, have common values so they are probably misused. + + // Case 2: + enum Bitmask { + A = 0, + B = 1, + C = 2, + D = 4, + E = 8, + F = 16, + G = 31 // OK, real bitmask. + }; + + enum Almostbitmask { + AA = 0, + BB = 1, + CC = 2, + DD = 4, + EE = 8, + FF = 16, + GG // Problem, forgot to initialize. + }; + + unsigned flag = 0; + flag |= E; // OK. + flag |= + EE; // Warning at the decl, and note that it was used here as a bitmask. + +Options +------- +.. option:: StrictMode + + Default value: 0. + When non-null the suspicious bitmask usage will be investigated additionally + to the different enum usage check. Index: docs/clang-tidy/checks/bugprone-suspicious-missing-comma.rst =================================================================== --- docs/clang-tidy/checks/bugprone-suspicious-missing-comma.rst +++ docs/clang-tidy/checks/bugprone-suspicious-missing-comma.rst @@ -0,0 +1,59 @@ +.. title:: clang-tidy - bugprone-suspicious-missing-comma + +bugprone-suspicious-missing-comma +================================= + +String literals placed side-by-side are concatenated at translation phase 6 +(after the preprocessor). This feature is used to represent long string +literal on multiple lines. + +For instance, the following declarations are equivalent: + +.. code-block:: c++ + + const char* A[] = "This is a test"; + const char* B[] = "This" " is a " "test"; + +A common mistake done by programmers is to forget a comma between two string +literals in an array initializer list. + +.. code-block:: c++ + + const char* Test[] = { + "line 1", + "line 2" // Missing comma! + "line 3", + "line 4", + "line 5" + }; + +The array contains the string "line 2line3" at offset 1 (i.e. Test[1]). Clang +won't generate warnings at compile time. + +This check may warn incorrectly on cases like: + +.. code-block:: c++ + + const char* SupportedFormat[] = { + "Error %s", + "Code " PRIu64, // May warn here. + "Warning %s", + }; + +Options +------- + +.. option:: SizeThreshold + + An unsigned integer specifying the minimum size of a string literal to be + considered by the check. Default is `5U`. + +.. option:: RatioThreshold + + A string specifying the maximum threshold ratio [0, 1.0] of suspicious string + literals to be considered. Default is `".2"`. + +.. option:: MaxConcatenatedTokens + + An unsigned integer specifying the maximum number of concatenated tokens. + Default is `5U`. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -35,7 +35,11 @@ bugprone-move-forwarding-reference bugprone-multiple-statement-macro bugprone-string-constructor + bugprone-string-integer-assignment + bugprone-string-literal-with-embedded-nul + bugprone-suspicious-enum-usage bugprone-suspicious-memset-usage + bugprone-suspicious-missing-comma bugprone-throw-keyword-missing bugprone-undefined-memory-manipulation bugprone-use-after-move @@ -139,10 +143,6 @@ misc-sizeof-container misc-sizeof-expression misc-static-assert - misc-string-integer-assignment - misc-string-literal-with-embedded-nul - misc-suspicious-enum-usage - misc-suspicious-missing-comma misc-suspicious-semicolon misc-suspicious-string-compare misc-swapped-arguments Index: docs/clang-tidy/checks/misc-string-integer-assignment.rst =================================================================== --- docs/clang-tidy/checks/misc-string-integer-assignment.rst +++ docs/clang-tidy/checks/misc-string-integer-assignment.rst @@ -1,37 +0,0 @@ -.. title:: clang-tidy - misc-string-integer-assignment - -misc-string-integer-assignment -============================== - -The check finds assignments of an integer to ``std::basic_string`` -(``std::string``, ``std::wstring``, etc.). The source of the problem is the -following assignment operator of ``std::basic_string``: - -.. code-block:: c++ - - basic_string& operator=( CharT ch ); - -Numeric types can be implicitly casted to character types. - -.. code-block:: c++ - - std::string s; - int x = 5965; - s = 6; - s = x; - -Use the appropriate conversion functions or character literals. - -.. code-block:: c++ - - std::string s; - int x = 5965; - s = '6'; - s = std::to_string(x); - -In order to suppress false positives, use an explicit cast. - -.. code-block:: c++ - - std::string s; - s = static_cast(6); Index: docs/clang-tidy/checks/misc-string-literal-with-embedded-nul.rst =================================================================== --- docs/clang-tidy/checks/misc-string-literal-with-embedded-nul.rst +++ docs/clang-tidy/checks/misc-string-literal-with-embedded-nul.rst @@ -1,36 +0,0 @@ -.. title:: clang-tidy - misc-string-literal-with-embedded-nul - -misc-string-literal-with-embedded-nul -===================================== - -Finds occurrences of string literal with embedded NUL character and validates -their usage. - -Invalid escaping ----------------- - -Special characters can be escaped within a string literal by using their -hexadecimal encoding like ``\x42``. A common mistake is to escape them -like this ``\0x42`` where the ``\0`` stands for the NUL character. - -.. code-block:: c++ - - const char* Example[] = "Invalid character: \0x12 should be \x12"; - const char* Bytes[] = "\x03\0x02\0x01\0x00\0xFF\0xFF\0xFF"; - -Truncated literal ------------------ - -String-like classes can manipulate strings with embedded NUL as they are keeping -track of the bytes and the length. This is not the case for a ``char*`` -(NUL-terminated) string. - -A common mistake is to pass a string-literal with embedded NUL to a string -constructor expecting a NUL-terminated string. The bytes after the first NUL -character are truncated. - -.. code-block:: c++ - - std::string str("abc\0def"); // "def" is truncated - str += "\0"; // This statement is doing nothing - if (str == "\0abc") return; // This expression is always true Index: docs/clang-tidy/checks/misc-suspicious-enum-usage.rst =================================================================== --- docs/clang-tidy/checks/misc-suspicious-enum-usage.rst +++ docs/clang-tidy/checks/misc-suspicious-enum-usage.rst @@ -1,78 +0,0 @@ -.. title:: clang-tidy - misc-suspicious-enum-usage - -misc-suspicious-enum-usage -========================== - -The checker detects various cases when an enum is probably misused (as a bitmask -). - -1. When "ADD" or "bitwise OR" is used between two enum which come from different - types and these types value ranges are not disjoint. - -The following cases will be investigated only using :option:`StrictMode`. We -regard the enum as a (suspicious) -bitmask if the three conditions below are true at the same time: - -* at most half of the elements of the enum are non pow-of-2 numbers (because of - short enumerations) -* there is another non pow-of-2 number than the enum constant representing all - choices (the result "bitwise OR" operation of all enum elements) -* enum type variable/enumconstant is used as an argument of a `+` or "bitwise OR - " operator - -So whenever the non pow-of-2 element is used as a bitmask element we diagnose a -misuse and give a warning. - -2. Investigating the right hand side of `+=` and `|=` operator. -3. Check only the enum value side of a `|` and `+` operator if one of them is not - enum val. -4. Check both side of `|` or `+` operator where the enum values are from the - same enum type. - -Examples: - -.. code-block:: c++ - - enum { A, B, C }; - enum { D, E, F = 5 }; - enum { G = 10, H = 11, I = 12 }; - - unsigned flag; - flag = - A | - H; // OK, disjoint value intervalls in the enum types ->probably good use. - flag = B | F; // Warning, have common values so they are probably misused. - - // Case 2: - enum Bitmask { - A = 0, - B = 1, - C = 2, - D = 4, - E = 8, - F = 16, - G = 31 // OK, real bitmask. - }; - - enum Almostbitmask { - AA = 0, - BB = 1, - CC = 2, - DD = 4, - EE = 8, - FF = 16, - GG // Problem, forgot to initialize. - }; - - unsigned flag = 0; - flag |= E; // OK. - flag |= - EE; // Warning at the decl, and note that it was used here as a bitmask. - -Options -------- -.. option:: StrictMode - - Default value: 0. - When non-null the suspicious bitmask usage will be investigated additionally - to the different enum usage check. Index: docs/clang-tidy/checks/misc-suspicious-missing-comma.rst =================================================================== --- docs/clang-tidy/checks/misc-suspicious-missing-comma.rst +++ docs/clang-tidy/checks/misc-suspicious-missing-comma.rst @@ -1,59 +0,0 @@ -.. title:: clang-tidy - misc-suspicious-missing-comma - -misc-suspicious-missing-comma -============================= - -String literals placed side-by-side are concatenated at translation phase 6 -(after the preprocessor). This feature is used to represent long string -literal on multiple lines. - -For instance, the following declarations are equivalent: - -.. code-block:: c++ - - const char* A[] = "This is a test"; - const char* B[] = "This" " is a " "test"; - -A common mistake done by programmers is to forget a comma between two string -literals in an array initializer list. - -.. code-block:: c++ - - const char* Test[] = { - "line 1", - "line 2" // Missing comma! - "line 3", - "line 4", - "line 5" - }; - -The array contains the string "line 2line3" at offset 1 (i.e. Test[1]). Clang -won't generate warnings at compile time. - -This check may warn incorrectly on cases like: - -.. code-block:: c++ - - const char* SupportedFormat[] = { - "Error %s", - "Code " PRIu64, // May warn here. - "Warning %s", - }; - -Options -------- - -.. option:: SizeThreshold - - An unsigned integer specifying the minimum size of a string literal to be - considered by the check. Default is `5U`. - -.. option:: RatioThreshold - - A string specifying the maximum threshold ratio [0, 1.0] of suspicious string - literals to be considered. Default is `".2"`. - -.. option:: MaxConcatenatedTokens - - An unsigned integer specifying the maximum number of concatenated tokens. - Default is `5U`. Index: test/clang-tidy/bugprone-string-integer-assignment.cpp =================================================================== --- test/clang-tidy/bugprone-string-integer-assignment.cpp +++ test/clang-tidy/bugprone-string-integer-assignment.cpp @@ -0,0 +1,53 @@ +// RUN: %check_clang_tidy %s bugprone-string-integer-assignment %t + +namespace std { +template +struct basic_string { + basic_string& operator=(T); + basic_string& operator=(basic_string); + basic_string& operator+=(T); + basic_string& operator+=(basic_string); +}; + +typedef basic_string string; +typedef basic_string wstring; +} + +typedef int MyArcaneChar; + +int main() { + std::string s; + std::wstring ws; + int x = 5; + + s = 6; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a character code when assigning {{.*}} [bugprone-string-integer-assignment] +// CHECK-FIXES: {{^}} s = '6';{{$}} + s = 66; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} s = "66";{{$}} + s = x; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} s = std::to_string(x);{{$}} + s = 'c'; + s = static_cast(6); + +// += + ws += 6; +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} ws += L'6';{{$}} + ws += 66; +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} ws += L"66";{{$}} + ws += x; +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} ws += std::to_wstring(x);{{$}} + ws += L'c'; + ws += (wchar_t)6; + + std::basic_string as; + as = 6; +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} as = 6;{{$}} + +} Index: test/clang-tidy/bugprone-string-literal-with-embedded-nul.cpp =================================================================== --- test/clang-tidy/bugprone-string-literal-with-embedded-nul.cpp +++ test/clang-tidy/bugprone-string-literal-with-embedded-nul.cpp @@ -0,0 +1,85 @@ +// RUN: %check_clang_tidy %s bugprone-string-literal-with-embedded-nul %t + +namespace std { +template +class allocator {}; +template +class char_traits {}; +template +struct basic_string { + typedef basic_string _Type; + basic_string(); + basic_string(const C *p, const A &a = A()); + + _Type& operator+=(const C* s); + _Type& operator=(const C* s); +}; + +typedef basic_string, std::allocator> string; +typedef basic_string, std::allocator> wstring; +} + +bool operator==(const std::string&, const char*); +bool operator==(const char*, const std::string&); + + +const char Valid[] = "This is valid \x12."; +const char Strange[] = "This is strange \0x12 and must be fixed"; +// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: suspicious embedded NUL character [bugprone-string-literal-with-embedded-nul] + +const char textA[] = "\0x01\0x02\0x03\0x04"; +// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious embedded NUL character +const wchar_t textW[] = L"\0x01\0x02\0x03\0x04"; +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious embedded NUL character + +const char A[] = "\0"; +const char B[] = "\0x"; +const char C[] = "\0x1"; +const char D[] = "\0x11"; +// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: suspicious embedded NUL character + +const wchar_t E[] = L"\0"; +const wchar_t F[] = L"\0x"; +const wchar_t G[] = L"\0x1"; +const wchar_t H[] = L"\0x11"; +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious embedded NUL character + +const char I[] = "\000\000\000\000"; +const char J[] = "\0\0\0\0\0\0"; +const char K[] = ""; + +const char L[] = "\0x12" "\0x12" "\0x12" "\0x12"; +// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: suspicious embedded NUL character + +void TestA() { + std::string str1 = "abc\0def"; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: truncated string literal + std::string str2 = "\0"; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: truncated string literal + std::string str3("\0"); + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: truncated string literal + std::string str4{"\x00\x01\x02\x03"}; + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: truncated string literal + + std::string str; + str += "abc\0def"; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: truncated string literal + str = "abc\0def"; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: truncated string literal + + if (str == "abc\0def") return; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: truncated string literal + if ("abc\0def" == str) return; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: truncated string literal +} + +void TestW() { + std::wstring str1 = L"abc\0def"; + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: truncated string literal + std::wstring str2 = L"\0"; + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: truncated string literal + std::wstring str3(L"\0"); + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: truncated string literal + std::wstring str4{L"\x00\x01\x02\x03"}; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: truncated string literal +} Index: test/clang-tidy/bugprone-suspicious-enum-usage-strict.cpp =================================================================== --- test/clang-tidy/bugprone-suspicious-enum-usage-strict.cpp +++ test/clang-tidy/bugprone-suspicious-enum-usage-strict.cpp @@ -0,0 +1,98 @@ +// RUN: %check_clang_tidy %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: [{key: bugprone-suspicious-enum-usage.StrictMode, value: 1}]}" -- + +enum A { + A = 1, + B = 2, + C = 4, + D = 8, + E = 16, + F = 32, + G = 63 +}; + +// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but a literal is not power-of-2 +// CHECK-MESSAGES: :76:7: note: used here as a bitmask +enum X { + X = 8, + Y = 16, + Z = 4, + ZZ = 3 + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2 [bugprone-suspicious-enum-usage] +// CHECK-MESSAGES: :70:13: note: used here as a bitmask +}; +// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but some literals are not power-of-2 +// CHECK-MESSAGES: :73:8: note: used here as a bitmask +enum PP { + P = 2, + Q = 3, + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2 + // CHECK-MESSAGES: :65:11: note: used here as a bitmask + R = 4, + S = 8, + T = 16, + U = 31 +}; + +enum { + H, + I, + J, + K, + L +}; + +enum Days { + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + Sunday +}; + +Days bestDay() { + return Friday; +} + +int trigger() { + if (bestDay() | A) + return 1; + // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: enum values are from different enum types + if (I | Y) + return 1; + // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: enum values are from different enum types + if (P + Q == R) + return 1; + else if ((S | R) == T) + return 1; + else + int k = ZZ | Z; + unsigned p = R; + PP pp = Q; + p |= pp; + + enum X x = Z; + p = x | Z; + return 0; +} + +int dont_trigger() { + int a = 1, b = 5; + int c = a + b; + int d = c | H, e = b * a; + a = B | C; + b = X | Z; + + unsigned bitflag; + enum A aa = B; + bitflag = aa | C; + + if (Tuesday != Monday + 1 || + Friday - Thursday != 1 || + Sunday + Wednesday == (Sunday | Wednesday)) + return 1; + if (H + I + L == 42) + return 1; + return 42; +} Index: test/clang-tidy/bugprone-suspicious-enum-usage.cpp =================================================================== --- test/clang-tidy/bugprone-suspicious-enum-usage.cpp +++ test/clang-tidy/bugprone-suspicious-enum-usage.cpp @@ -0,0 +1,96 @@ +// RUN: %check_clang_tidy %s bugprone-suspicious-enum-usage %t -- -config="{CheckOptions: [{key: bugprone-suspicious-enum-usage.StrictMode, value: 0}]}" -- + +enum Empty { +}; + +enum A { + A = 1, + B = 2, + C = 4, + D = 8, + E = 16, + F = 32, + G = 63 +}; + +enum X { + X = 8, + Y = 16, + Z = 4 +}; + +enum { + P = 2, + Q = 3, + R = 4, + S = 8, + T = 16 +}; + +enum { + H, + I, + J, + K, + L +}; + +enum Days { + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + Sunday +}; + +Days bestDay() { + return Friday; +} + +int trigger() { + Empty EmptyVal; + int emptytest = EmptyVal | B; + if (bestDay() | A) + return 1; + // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: enum values are from different enum types + if (I | Y) + return 1; + // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: enum values are from different enum types +} + +int dont_trigger() { + unsigned p; + p = Q | P; + + if (A + G == E) + return 1; + else if ((Q | R) == T) + return 1; + else + int k = T | Q; + + Empty EmptyVal; + int emptytest = EmptyVal | B; + + int a = 1, b = 5; + int c = a + b; + int d = c | H, e = b * a; + a = B | C; + b = X | Z; + + if (Tuesday != Monday + 1 || + Friday - Thursday != 1 || + Sunday + Wednesday == (Sunday | Wednesday)) + return 1; + if (H + I + L == 42) + return 1; + return 42; +} + +namespace PR34400 { +enum { E1 = 0 }; +enum { E2 = -1 }; +enum { l = E1 | E2 }; +} Index: test/clang-tidy/bugprone-suspicious-missing-comma.cpp =================================================================== --- test/clang-tidy/bugprone-suspicious-missing-comma.cpp +++ test/clang-tidy/bugprone-suspicious-missing-comma.cpp @@ -0,0 +1,82 @@ +// RUN: %check_clang_tidy %s bugprone-suspicious-missing-comma %t + +const char* Cartoons[] = { + "Bugs Bunny", + "Homer Simpson", + "Mickey Mouse", + "Bart Simpson", + "Charlie Brown" // There is a missing comma here. + "Fred Flintstone", + "Popeye", +}; +// CHECK-MESSAGES: :[[@LINE-4]]:3: warning: suspicious string literal, probably missing a comma [bugprone-suspicious-missing-comma] + +const wchar_t* Colors[] = { + L"Red", L"Yellow", L"Blue", L"Green", L"Purple", L"Rose", L"White", L"Black" +}; + +// The following array should not trigger any warnings. There is more than 5 +// elements, but they are all concatenated string literals. +const char* HttpCommands[] = { + "GET / HTTP/1.0\r\n" + "\r\n", + + "GET /index.html HTTP/1.0\r\n" + "\r\n", + + "GET /favicon.ico HTTP/1.0\r\n" + "header: dummy" + "\r\n", + + "GET /index.html-en HTTP/1.0\r\n" + "\r\n", + + "GET /index.html-fr HTTP/1.0\r\n" + "\r\n", + + "GET /index.html-es HTTP/1.0\r\n" + "\r\n", +}; + +// This array is too small to trigger a warning. +const char* SmallArray[] = { + "a" "b", "c" +}; + +// Parentheses should be enough to avoid warnings. +const char* ParentheseArray[] = { + ("a" "b"), "c", + ("d" + "e" + "f"), + "g", "h", "i", "j", "k", "l" +}; + +// Indentation should be enough to avoid warnings. +const char* CorrectlyIndentedArray[] = { + "This is a long message " + "which is spanning over multiple lines." + "And this should be fine.", + "a", "b", "c", "d", "e", "f", + "g", "h", "i", "j", "k", "l" +}; + +const char* IncorrectlyIndentedArray[] = { + "This is a long message " + "which is spanning over multiple lines." + "And this should be fine.", + "a", "b", "c", "d", "e", "f", + "g", "h", "i", "j", "k", "l" +}; +// CHECK-MESSAGES: :[[@LINE-6]]:3: warning: suspicious string literal, probably missing a comma [bugprone-suspicious-missing-comma] + +const char* TooManyConcatenatedTokensArray[] = { + "Dummy line", + "Dummy line", + "a" "b" "c" "d" "e" "f", + "g" "h" "i" "j" "k" "l", + "Dummy line", + "Dummy line", + "Dummy line", + "Dummy line", +}; Index: test/clang-tidy/misc-string-integer-assignment.cpp =================================================================== --- test/clang-tidy/misc-string-integer-assignment.cpp +++ test/clang-tidy/misc-string-integer-assignment.cpp @@ -1,53 +0,0 @@ -// RUN: %check_clang_tidy %s misc-string-integer-assignment %t - -namespace std { -template -struct basic_string { - basic_string& operator=(T); - basic_string& operator=(basic_string); - basic_string& operator+=(T); - basic_string& operator+=(basic_string); -}; - -typedef basic_string string; -typedef basic_string wstring; -} - -typedef int MyArcaneChar; - -int main() { - std::string s; - std::wstring ws; - int x = 5; - - s = 6; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a character code when assigning {{.*}} [misc-string-integer-assignment] -// CHECK-FIXES: {{^}} s = '6';{{$}} - s = 66; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a chara -// CHECK-FIXES: {{^}} s = "66";{{$}} - s = x; -// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a chara -// CHECK-FIXES: {{^}} s = std::to_string(x);{{$}} - s = 'c'; - s = static_cast(6); - -// += - ws += 6; -// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara -// CHECK-FIXES: {{^}} ws += L'6';{{$}} - ws += 66; -// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara -// CHECK-FIXES: {{^}} ws += L"66";{{$}} - ws += x; -// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara -// CHECK-FIXES: {{^}} ws += std::to_wstring(x);{{$}} - ws += L'c'; - ws += (wchar_t)6; - - std::basic_string as; - as = 6; -// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: an integer is interpreted as a chara -// CHECK-FIXES: {{^}} as = 6;{{$}} - -} Index: test/clang-tidy/misc-string-literal-with-embedded-nul.cpp =================================================================== --- test/clang-tidy/misc-string-literal-with-embedded-nul.cpp +++ test/clang-tidy/misc-string-literal-with-embedded-nul.cpp @@ -1,85 +0,0 @@ -// RUN: %check_clang_tidy %s misc-string-literal-with-embedded-nul %t - -namespace std { -template -class allocator {}; -template -class char_traits {}; -template -struct basic_string { - typedef basic_string _Type; - basic_string(); - basic_string(const C *p, const A &a = A()); - - _Type& operator+=(const C* s); - _Type& operator=(const C* s); -}; - -typedef basic_string, std::allocator> string; -typedef basic_string, std::allocator> wstring; -} - -bool operator==(const std::string&, const char*); -bool operator==(const char*, const std::string&); - - -const char Valid[] = "This is valid \x12."; -const char Strange[] = "This is strange \0x12 and must be fixed"; -// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: suspicious embedded NUL character [misc-string-literal-with-embedded-nul] - -const char textA[] = "\0x01\0x02\0x03\0x04"; -// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: suspicious embedded NUL character -const wchar_t textW[] = L"\0x01\0x02\0x03\0x04"; -// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: suspicious embedded NUL character - -const char A[] = "\0"; -const char B[] = "\0x"; -const char C[] = "\0x1"; -const char D[] = "\0x11"; -// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: suspicious embedded NUL character - -const wchar_t E[] = L"\0"; -const wchar_t F[] = L"\0x"; -const wchar_t G[] = L"\0x1"; -const wchar_t H[] = L"\0x11"; -// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: suspicious embedded NUL character - -const char I[] = "\000\000\000\000"; -const char J[] = "\0\0\0\0\0\0"; -const char K[] = ""; - -const char L[] = "\0x12" "\0x12" "\0x12" "\0x12"; -// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: suspicious embedded NUL character - -void TestA() { - std::string str1 = "abc\0def"; - // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: truncated string literal - std::string str2 = "\0"; - // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: truncated string literal - std::string str3("\0"); - // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: truncated string literal - std::string str4{"\x00\x01\x02\x03"}; - // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: truncated string literal - - std::string str; - str += "abc\0def"; - // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: truncated string literal - str = "abc\0def"; - // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: truncated string literal - - if (str == "abc\0def") return; - // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: truncated string literal - if ("abc\0def" == str) return; - // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: truncated string literal -} - -void TestW() { - std::wstring str1 = L"abc\0def"; - // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: truncated string literal - std::wstring str2 = L"\0"; - // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: truncated string literal - std::wstring str3(L"\0"); - // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: truncated string literal - std::wstring str4{L"\x00\x01\x02\x03"}; - // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: truncated string literal -} Index: test/clang-tidy/misc-suspicious-enum-usage-strict.cpp =================================================================== --- test/clang-tidy/misc-suspicious-enum-usage-strict.cpp +++ test/clang-tidy/misc-suspicious-enum-usage-strict.cpp @@ -1,98 +0,0 @@ -// RUN: %check_clang_tidy %s misc-suspicious-enum-usage %t -- -config="{CheckOptions: [{key: misc-suspicious-enum-usage.StrictMode, value: 1}]}" -- - -enum A { - A = 1, - B = 2, - C = 4, - D = 8, - E = 16, - F = 32, - G = 63 -}; - -// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but a literal is not power-of-2 -// CHECK-MESSAGES: :76:7: note: used here as a bitmask -enum X { - X = 8, - Y = 16, - Z = 4, - ZZ = 3 - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2 [misc-suspicious-enum-usage] -// CHECK-MESSAGES: :70:13: note: used here as a bitmask -}; -// CHECK-MESSAGES: :[[@LINE+2]]:1: warning: enum type seems like a bitmask (contains mostly power-of-2 literals) but some literals are not power-of-2 -// CHECK-MESSAGES: :73:8: note: used here as a bitmask -enum PP { - P = 2, - Q = 3, - // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: enum type seems like a bitmask (contains mostly power-of-2 literals), but this literal is not a power-of-2 - // CHECK-MESSAGES: :65:11: note: used here as a bitmask - R = 4, - S = 8, - T = 16, - U = 31 -}; - -enum { - H, - I, - J, - K, - L -}; - -enum Days { - Monday, - Tuesday, - Wednesday, - Thursday, - Friday, - Saturday, - Sunday -}; - -Days bestDay() { - return Friday; -} - -int trigger() { - if (bestDay() | A) - return 1; - // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: enum values are from different enum types - if (I | Y) - return 1; - // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: enum values are from different enum types - if (P + Q == R) - return 1; - else if ((S | R) == T) - return 1; - else - int k = ZZ | Z; - unsigned p = R; - PP pp = Q; - p |= pp; - - enum X x = Z; - p = x | Z; - return 0; -} - -int dont_trigger() { - int a = 1, b = 5; - int c = a + b; - int d = c | H, e = b * a; - a = B | C; - b = X | Z; - - unsigned bitflag; - enum A aa = B; - bitflag = aa | C; - - if (Tuesday != Monday + 1 || - Friday - Thursday != 1 || - Sunday + Wednesday == (Sunday | Wednesday)) - return 1; - if (H + I + L == 42) - return 1; - return 42; -} Index: test/clang-tidy/misc-suspicious-enum-usage.cpp =================================================================== --- test/clang-tidy/misc-suspicious-enum-usage.cpp +++ test/clang-tidy/misc-suspicious-enum-usage.cpp @@ -1,96 +0,0 @@ -// RUN: %check_clang_tidy %s misc-suspicious-enum-usage %t -- -config="{CheckOptions: [{key: misc-suspicious-enum-usage.StrictMode, value: 0}]}" -- - -enum Empty { -}; - -enum A { - A = 1, - B = 2, - C = 4, - D = 8, - E = 16, - F = 32, - G = 63 -}; - -enum X { - X = 8, - Y = 16, - Z = 4 -}; - -enum { - P = 2, - Q = 3, - R = 4, - S = 8, - T = 16 -}; - -enum { - H, - I, - J, - K, - L -}; - -enum Days { - Monday, - Tuesday, - Wednesday, - Thursday, - Friday, - Saturday, - Sunday -}; - -Days bestDay() { - return Friday; -} - -int trigger() { - Empty EmptyVal; - int emptytest = EmptyVal | B; - if (bestDay() | A) - return 1; - // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: enum values are from different enum types - if (I | Y) - return 1; - // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: enum values are from different enum types -} - -int dont_trigger() { - unsigned p; - p = Q | P; - - if (A + G == E) - return 1; - else if ((Q | R) == T) - return 1; - else - int k = T | Q; - - Empty EmptyVal; - int emptytest = EmptyVal | B; - - int a = 1, b = 5; - int c = a + b; - int d = c | H, e = b * a; - a = B | C; - b = X | Z; - - if (Tuesday != Monday + 1 || - Friday - Thursday != 1 || - Sunday + Wednesday == (Sunday | Wednesday)) - return 1; - if (H + I + L == 42) - return 1; - return 42; -} - -namespace PR34400 { -enum { E1 = 0 }; -enum { E2 = -1 }; -enum { l = E1 | E2 }; -} Index: test/clang-tidy/misc-suspicious-missing-comma.cpp =================================================================== --- test/clang-tidy/misc-suspicious-missing-comma.cpp +++ test/clang-tidy/misc-suspicious-missing-comma.cpp @@ -1,82 +0,0 @@ -// RUN: %check_clang_tidy %s misc-suspicious-missing-comma %t - -const char* Cartoons[] = { - "Bugs Bunny", - "Homer Simpson", - "Mickey Mouse", - "Bart Simpson", - "Charlie Brown" // There is a missing comma here. - "Fred Flintstone", - "Popeye", -}; -// CHECK-MESSAGES: :[[@LINE-4]]:3: warning: suspicious string literal, probably missing a comma [misc-suspicious-missing-comma] - -const wchar_t* Colors[] = { - L"Red", L"Yellow", L"Blue", L"Green", L"Purple", L"Rose", L"White", L"Black" -}; - -// The following array should not trigger any warnings. There is more than 5 -// elements, but they are all concatenated string literals. -const char* HttpCommands[] = { - "GET / HTTP/1.0\r\n" - "\r\n", - - "GET /index.html HTTP/1.0\r\n" - "\r\n", - - "GET /favicon.ico HTTP/1.0\r\n" - "header: dummy" - "\r\n", - - "GET /index.html-en HTTP/1.0\r\n" - "\r\n", - - "GET /index.html-fr HTTP/1.0\r\n" - "\r\n", - - "GET /index.html-es HTTP/1.0\r\n" - "\r\n", -}; - -// This array is too small to trigger a warning. -const char* SmallArray[] = { - "a" "b", "c" -}; - -// Parentheses should be enough to avoid warnings. -const char* ParentheseArray[] = { - ("a" "b"), "c", - ("d" - "e" - "f"), - "g", "h", "i", "j", "k", "l" -}; - -// Indentation should be enough to avoid warnings. -const char* CorrectlyIndentedArray[] = { - "This is a long message " - "which is spanning over multiple lines." - "And this should be fine.", - "a", "b", "c", "d", "e", "f", - "g", "h", "i", "j", "k", "l" -}; - -const char* IncorrectlyIndentedArray[] = { - "This is a long message " - "which is spanning over multiple lines." - "And this should be fine.", - "a", "b", "c", "d", "e", "f", - "g", "h", "i", "j", "k", "l" -}; -// CHECK-MESSAGES: :[[@LINE-6]]:3: warning: suspicious string literal, probably missing a comma [misc-suspicious-missing-comma] - -const char* TooManyConcatenatedTokensArray[] = { - "Dummy line", - "Dummy line", - "a" "b" "c" "d" "e" "f", - "g" "h" "i" "j" "k" "l", - "Dummy line", - "Dummy line", - "Dummy line", - "Dummy line", -};