57 const auto ZeroExpr = expr(ignoringParenImpCasts(integerLiteral(equals(0))));
58 const auto CharExpr = expr(ignoringParenImpCasts(characterLiteral()));
59 const auto NegativeExpr = expr(ignoringParenImpCasts(
60 unaryOperator(hasOperatorName(
"-"),
61 hasUnaryOperand(integerLiteral(unless(equals(0)))))));
62 const auto LargeLengthExpr = expr(ignoringParenImpCasts(
63 integerLiteral(isBiggerThan(LargeLengthThreshold))));
64 const auto CharPtrType = type(anyOf(pointerType(), arrayType()));
67 const auto BoundStringLiteral = stringLiteral().bind(
"str");
68 const auto ConstStrLiteralDecl = varDecl(
69 isDefinition(), hasType(constantArrayType()), hasType(isConstQualified()),
70 hasInitializer(ignoringParenImpCasts(BoundStringLiteral)));
71 const auto ConstPtrStrLiteralDecl = varDecl(
73 hasType(pointerType(pointee(isAnyCharacter(), isConstQualified()))),
74 hasInitializer(ignoringParenImpCasts(BoundStringLiteral)));
75 const auto ConstStrLiteral = expr(ignoringParenImpCasts(anyOf(
76 BoundStringLiteral, declRefExpr(hasDeclaration(anyOf(
77 ConstPtrStrLiteralDecl, ConstStrLiteralDecl))))));
83 hasDeclaration(cxxMethodDecl(hasName(
"basic_string"))),
84 argumentCountIs(2), hasArgument(0, hasType(qualType(isInteger()))),
85 hasArgument(1, hasType(qualType(isInteger()))),
88 hasArgument(0, CharExpr.bind(
"swapped-parameter")),
90 hasArgument(0, ZeroExpr.bind(
"empty-string")),
92 hasArgument(0, NegativeExpr.bind(
"negative-length")),
94 hasArgument(0, LargeLengthExpr.bind(
"large-length"))))
102 hasDeclaration(cxxConstructorDecl(ofClass(
104 argumentCountIs(2), hasArgument(0, hasType(CharPtrType)),
105 hasArgument(1, hasType(isInteger())),
108 hasArgument(1, ZeroExpr.bind(
"empty-string")),
110 hasArgument(1, NegativeExpr.bind(
"negative-length")),
112 hasArgument(1, LargeLengthExpr.bind(
"large-length")),
114 allOf(hasArgument(0, ConstStrLiteral.bind(
"literal-with-length")),
115 hasArgument(1, ignoringParenImpCasts(
116 integerLiteral().bind(
"length"))))))
117 .bind(
"constructor"),
124 hasDeclaration(cxxConstructorDecl(ofClass(
126 argumentCountIs(3), hasArgument(0, hasType(CharPtrType)),
127 hasArgument(1, hasType(qualType(isInteger()))),
128 hasArgument(2, hasType(qualType(isInteger()))),
131 hasArgument(2, ZeroExpr.bind(
"empty-string")),
133 hasArgument(1, NegativeExpr.bind(
"negative-pos")),
135 hasArgument(2, NegativeExpr.bind(
"negative-length")),
137 hasArgument(2, LargeLengthExpr.bind(
"large-length")),
139 allOf(hasArgument(0, ConstStrLiteral.bind(
"literal-with-length")),
141 1, ignoringParenImpCasts(integerLiteral().bind(
"pos"))),
142 hasArgument(2, ignoringParenImpCasts(
143 integerLiteral().bind(
"length"))))))
144 .bind(
"constructor"),
153 hasDeclaration(cxxConstructorDecl(ofClass(anyOf(
154 cxxRecordDecl(hasName(
"basic_string_view"))
155 .bind(
"basic_string_view_decl"),
157 hasArgument(0, expr().bind(
"from-ptr")),
161 anyOf(hasArgument(1, unless(hasType(isInteger()))),
163 .bind(
"constructor")),
168 const ASTContext &Ctx = *Result.Context;
169 const auto *E = Result.Nodes.getNodeAs<CXXConstructExpr>(
"constructor");
170 assert(E &&
"missing constructor expression");
171 SourceLocation Loc = E->getBeginLoc();
173 if (Result.Nodes.getNodeAs<Expr>(
"swapped-parameter")) {
174 const Expr *P0 = E->getArg(0);
175 const Expr *P1 = E->getArg(1);
176 diag(Loc,
"string constructor parameters are probably swapped;"
177 " expecting string(count, character)")
178 << tooling::fixit::createReplacement(*P0, *P1, Ctx)
179 << tooling::fixit::createReplacement(*P1, *P0, Ctx);
180 }
else if (Result.Nodes.getNodeAs<Expr>(
"empty-string")) {
181 diag(Loc,
"constructor creating an empty string");
182 }
else if (Result.Nodes.getNodeAs<Expr>(
"negative-length")) {
183 diag(Loc,
"negative value used as length parameter");
184 }
else if (Result.Nodes.getNodeAs<Expr>(
"negative-pos")) {
185 diag(Loc,
"negative value used as position of the "
186 "first character parameter");
187 }
else if (Result.Nodes.getNodeAs<Expr>(
"large-length")) {
188 if (WarnOnLargeLength)
189 diag(Loc,
"suspicious large length parameter");
190 }
else if (Result.Nodes.getNodeAs<Expr>(
"literal-with-length")) {
191 const auto *Str = Result.Nodes.getNodeAs<StringLiteral>(
"str");
192 const auto *Length = Result.Nodes.getNodeAs<IntegerLiteral>(
"length");
193 if (Length->getValue().ugt(Str->getLength())) {
194 diag(Loc,
"length is bigger than string literal size");
197 if (
const auto *Pos = Result.Nodes.getNodeAs<IntegerLiteral>(
"pos")) {
198 if (Pos->getValue().uge(Str->getLength())) {
199 diag(Loc,
"position of the first character parameter is bigger than "
200 "string literal character range");
201 }
else if (Length->getValue().ugt(
202 (Str->getLength() - Pos->getValue()).getZExtValue())) {
203 diag(Loc,
"length is bigger than remaining string literal size");
206 }
else if (
const auto *Ptr = Result.Nodes.getNodeAs<Expr>(
"from-ptr")) {
207 Expr::EvalResult ConstPtr;
208 if (!Ptr->isInstantiationDependent() &&
209 Ptr->EvaluateAsRValue(ConstPtr, Ctx) &&
210 ((ConstPtr.Val.isInt() && ConstPtr.Val.getInt().isZero()) ||
211 (ConstPtr.Val.isLValue() && ConstPtr.Val.isNullPointer()))) {
212 if (IsStringviewNullptrCheckEnabled &&
213 Result.Nodes.getNodeAs<CXXRecordDecl>(
"basic_string_view_decl")) {
218 diag(Loc,
"constructing string from nullptr is undefined behaviour");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.