Etienne Bergeron | 1dbd582 | 2016-04-21 17:28:08 +0000 | [diff] [blame] | 1 | //===--- StringConstructorCheck.cpp - clang-tidy---------------------------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
| 10 | #include "StringConstructorCheck.h" |
| 11 | #include "clang/AST/ASTContext.h" |
| 12 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
Etienne Bergeron | e646a83 | 2016-05-11 17:32:12 +0000 | [diff] [blame] | 13 | #include "clang/Tooling/FixIt.h" |
Etienne Bergeron | 1dbd582 | 2016-04-21 17:28:08 +0000 | [diff] [blame] | 14 | |
| 15 | using namespace clang::ast_matchers; |
| 16 | |
| 17 | namespace clang { |
| 18 | namespace tidy { |
Alexander Kornienko | a3251bf | 2017-11-23 13:49:14 +0000 | [diff] [blame] | 19 | namespace bugprone { |
Etienne Bergeron | 1dbd582 | 2016-04-21 17:28:08 +0000 | [diff] [blame] | 20 | |
| 21 | AST_MATCHER_P(IntegerLiteral, isBiggerThan, unsigned, N) { |
| 22 | return Node.getValue().getZExtValue() > N; |
| 23 | } |
| 24 | |
| 25 | StringConstructorCheck::StringConstructorCheck(StringRef Name, |
| 26 | ClangTidyContext *Context) |
| 27 | : ClangTidyCheck(Name, Context), |
| 28 | WarnOnLargeLength(Options.get("WarnOnLargeLength", 1) != 0), |
| 29 | LargeLengthThreshold(Options.get("LargeLengthThreshold", 0x800000)) {} |
| 30 | |
| 31 | void StringConstructorCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
| 32 | Options.store(Opts, "WarnOnLargeLength", WarnOnLargeLength); |
| 33 | Options.store(Opts, "LargeLengthThreshold", LargeLengthThreshold); |
| 34 | } |
| 35 | |
| 36 | void StringConstructorCheck::registerMatchers(MatchFinder *Finder) { |
| 37 | if (!getLangOpts().CPlusPlus) |
| 38 | return; |
| 39 | |
| 40 | const auto ZeroExpr = expr(ignoringParenImpCasts(integerLiteral(equals(0)))); |
| 41 | const auto CharExpr = expr(ignoringParenImpCasts(characterLiteral())); |
| 42 | const auto NegativeExpr = expr(ignoringParenImpCasts( |
| 43 | unaryOperator(hasOperatorName("-"), |
| 44 | hasUnaryOperand(integerLiteral(unless(equals(0))))))); |
| 45 | const auto LargeLengthExpr = expr(ignoringParenImpCasts( |
| 46 | integerLiteral(isBiggerThan(LargeLengthThreshold)))); |
| 47 | const auto CharPtrType = type(anyOf(pointerType(), arrayType())); |
| 48 | |
| 49 | // Match a string-literal; even through a declaration with initializer. |
| 50 | const auto BoundStringLiteral = stringLiteral().bind("str"); |
| 51 | const auto ConstStrLiteralDecl = varDecl( |
| 52 | isDefinition(), hasType(constantArrayType()), hasType(isConstQualified()), |
| 53 | hasInitializer(ignoringParenImpCasts(BoundStringLiteral))); |
| 54 | const auto ConstPtrStrLiteralDecl = varDecl( |
| 55 | isDefinition(), |
| 56 | hasType(pointerType(pointee(isAnyCharacter(), isConstQualified()))), |
| 57 | hasInitializer(ignoringParenImpCasts(BoundStringLiteral))); |
Etienne Bergeron | e646a83 | 2016-05-11 17:32:12 +0000 | [diff] [blame] | 58 | const auto ConstStrLiteral = expr(ignoringParenImpCasts(anyOf( |
Etienne Bergeron | 1dbd582 | 2016-04-21 17:28:08 +0000 | [diff] [blame] | 59 | BoundStringLiteral, declRefExpr(hasDeclaration(anyOf( |
| 60 | ConstPtrStrLiteralDecl, ConstStrLiteralDecl)))))); |
| 61 | |
| 62 | // Check the fill constructor. Fills the string with n consecutive copies of |
| 63 | // character c. [i.e string(size_t n, char c);]. |
| 64 | Finder->addMatcher( |
| 65 | cxxConstructExpr( |
| 66 | hasDeclaration(cxxMethodDecl(hasName("basic_string"))), |
| 67 | hasArgument(0, hasType(qualType(isInteger()))), |
| 68 | hasArgument(1, hasType(qualType(isInteger()))), |
| 69 | anyOf( |
| 70 | // Detect the expression: string('x', 40); |
| 71 | hasArgument(0, CharExpr.bind("swapped-parameter")), |
| 72 | // Detect the expression: string(0, ...); |
| 73 | hasArgument(0, ZeroExpr.bind("empty-string")), |
| 74 | // Detect the expression: string(-4, ...); |
| 75 | hasArgument(0, NegativeExpr.bind("negative-length")), |
| 76 | // Detect the expression: string(0x1234567, ...); |
| 77 | hasArgument(0, LargeLengthExpr.bind("large-length")))) |
| 78 | .bind("constructor"), |
| 79 | this); |
| 80 | |
| 81 | // Check the literal string constructor with char pointer and length |
| 82 | // parameters. [i.e. string (const char* s, size_t n);] |
| 83 | Finder->addMatcher( |
| 84 | cxxConstructExpr( |
| 85 | hasDeclaration(cxxMethodDecl(hasName("basic_string"))), |
| 86 | hasArgument(0, hasType(CharPtrType)), |
| 87 | hasArgument(1, hasType(isInteger())), |
| 88 | anyOf( |
| 89 | // Detect the expression: string("...", 0); |
| 90 | hasArgument(1, ZeroExpr.bind("empty-string")), |
| 91 | // Detect the expression: string("...", -4); |
Etienne Bergeron | e646a83 | 2016-05-11 17:32:12 +0000 | [diff] [blame] | 92 | hasArgument(1, NegativeExpr.bind("negative-length")), |
Etienne Bergeron | 1dbd582 | 2016-04-21 17:28:08 +0000 | [diff] [blame] | 93 | // Detect the expression: string("lit", 0x1234567); |
| 94 | hasArgument(1, LargeLengthExpr.bind("large-length")), |
| 95 | // Detect the expression: string("lit", 5) |
| 96 | allOf(hasArgument(0, ConstStrLiteral.bind("literal-with-length")), |
| 97 | hasArgument(1, ignoringParenImpCasts( |
| 98 | integerLiteral().bind("int")))))) |
| 99 | .bind("constructor"), |
| 100 | this); |
| 101 | } |
| 102 | |
| 103 | void StringConstructorCheck::check(const MatchFinder::MatchResult &Result) { |
Etienne Bergeron | e646a83 | 2016-05-11 17:32:12 +0000 | [diff] [blame] | 104 | const ASTContext &Ctx = *Result.Context; |
| 105 | const auto *E = Result.Nodes.getNodeAs<CXXConstructExpr>("constructor"); |
| 106 | assert(E && "missing constructor expression"); |
Etienne Bergeron | 1dbd582 | 2016-04-21 17:28:08 +0000 | [diff] [blame] | 107 | SourceLocation Loc = E->getLocStart(); |
| 108 | |
| 109 | if (Result.Nodes.getNodeAs<Expr>("swapped-parameter")) { |
Etienne Bergeron | e646a83 | 2016-05-11 17:32:12 +0000 | [diff] [blame] | 110 | const Expr *P0 = E->getArg(0); |
| 111 | const Expr *P1 = E->getArg(1); |
| 112 | diag(Loc, "string constructor parameters are probably swapped;" |
| 113 | " expecting string(count, character)") |
| 114 | << tooling::fixit::createReplacement(*P0, *P1, Ctx) |
| 115 | << tooling::fixit::createReplacement(*P1, *P0, Ctx); |
Etienne Bergeron | 1dbd582 | 2016-04-21 17:28:08 +0000 | [diff] [blame] | 116 | } else if (Result.Nodes.getNodeAs<Expr>("empty-string")) { |
| 117 | diag(Loc, "constructor creating an empty string"); |
| 118 | } else if (Result.Nodes.getNodeAs<Expr>("negative-length")) { |
| 119 | diag(Loc, "negative value used as length parameter"); |
| 120 | } else if (Result.Nodes.getNodeAs<Expr>("large-length")) { |
| 121 | if (WarnOnLargeLength) |
| 122 | diag(Loc, "suspicious large length parameter"); |
| 123 | } else if (Result.Nodes.getNodeAs<Expr>("literal-with-length")) { |
| 124 | const auto *Str = Result.Nodes.getNodeAs<StringLiteral>("str"); |
| 125 | const auto *Lit = Result.Nodes.getNodeAs<IntegerLiteral>("int"); |
| 126 | if (Lit->getValue().ugt(Str->getLength())) { |
| 127 | diag(Loc, "length is bigger then string literal size"); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
Alexander Kornienko | a3251bf | 2017-11-23 13:49:14 +0000 | [diff] [blame] | 132 | } // namespace bugprone |
Etienne Bergeron | 1dbd582 | 2016-04-21 17:28:08 +0000 | [diff] [blame] | 133 | } // namespace tidy |
| 134 | } // namespace clang |