58 const auto ZeroExpr = expr(ignoringParenImpCasts(integerLiteral(equals(0))));
59 const auto CharExpr = expr(ignoringParenImpCasts(characterLiteral()));
60 const auto NegativeExpr = expr(ignoringParenImpCasts(
61 unaryOperator(hasOperatorName(
"-"),
62 hasUnaryOperand(integerLiteral(unless(equals(0)))))));
63 const auto LargeLengthExpr = expr(ignoringParenImpCasts(
64 integerLiteral(isBiggerThan(LargeLengthThreshold))));
65 const auto CharPtrType = type(anyOf(pointerType(), arrayType()));
68 const auto BoundStringLiteral = stringLiteral().bind(
"str");
69 const auto ConstStrLiteralDecl = varDecl(
70 isDefinition(), hasType(constantArrayType()), hasType(isConstQualified()),
71 hasInitializer(ignoringParenImpCasts(BoundStringLiteral)));
72 const auto ConstPtrStrLiteralDecl = varDecl(
74 hasType(pointerType(pointee(isAnyCharacter(), isConstQualified()))),
75 hasInitializer(ignoringParenImpCasts(BoundStringLiteral)));
76 const auto ConstStrLiteral = expr(ignoringParenImpCasts(anyOf(
77 BoundStringLiteral, declRefExpr(hasDeclaration(anyOf(
78 ConstPtrStrLiteralDecl, ConstStrLiteralDecl))))));
84 hasDeclaration(cxxMethodDecl(hasName(
"basic_string"))),
85 argumentCountIs(2), hasArgument(0, hasType(qualType(isInteger()))),
86 hasArgument(1, hasType(qualType(isInteger()))),
89 hasArgument(0, CharExpr.bind(
"swapped-parameter")),
91 hasArgument(0, ZeroExpr.bind(
"empty-string")),
93 hasArgument(0, NegativeExpr.bind(
"negative-length")),
95 hasArgument(0, LargeLengthExpr.bind(
"large-length"))))
103 hasDeclaration(cxxConstructorDecl(ofClass(
104 cxxRecordDecl(hasAnyName(removeNamespaces(StringNames)))))),
105 argumentCountIs(2), hasArgument(0, hasType(CharPtrType)),
106 hasArgument(1, hasType(isInteger())),
109 hasArgument(1, ZeroExpr.bind(
"empty-string")),
111 hasArgument(1, NegativeExpr.bind(
"negative-length")),
113 hasArgument(1, LargeLengthExpr.bind(
"large-length")),
115 allOf(hasArgument(0, ConstStrLiteral.bind(
"literal-with-length")),
116 hasArgument(1, ignoringParenImpCasts(
117 integerLiteral().bind(
"length"))))))
118 .bind(
"constructor"),
125 hasDeclaration(cxxConstructorDecl(ofClass(
126 cxxRecordDecl(hasAnyName(removeNamespaces(StringNames)))))),
127 argumentCountIs(3), hasArgument(0, hasType(CharPtrType)),
128 hasArgument(1, hasType(qualType(isInteger()))),
129 hasArgument(2, hasType(qualType(isInteger()))),
132 hasArgument(2, ZeroExpr.bind(
"empty-string")),
134 hasArgument(1, NegativeExpr.bind(
"negative-pos")),
136 hasArgument(2, NegativeExpr.bind(
"negative-length")),
138 hasArgument(2, LargeLengthExpr.bind(
"large-length")),
140 allOf(hasArgument(0, ConstStrLiteral.bind(
"literal-with-length")),
142 1, ignoringParenImpCasts(integerLiteral().bind(
"pos"))),
143 hasArgument(2, ignoringParenImpCasts(
144 integerLiteral().bind(
"length"))))))
145 .bind(
"constructor"),
154 hasDeclaration(cxxConstructorDecl(ofClass(anyOf(
155 cxxRecordDecl(hasName(
"basic_string_view"))
156 .bind(
"basic_string_view_decl"),
157 cxxRecordDecl(hasAnyName(removeNamespaces(StringNames))))))),
158 hasArgument(0, expr().bind(
"from-ptr")),
162 anyOf(hasArgument(1, unless(hasType(isInteger()))),
164 .bind(
"constructor")),
169 const ASTContext &Ctx = *Result.Context;
170 const auto *E = Result.Nodes.getNodeAs<CXXConstructExpr>(
"constructor");
171 assert(E &&
"missing constructor expression");
172 SourceLocation Loc = E->getBeginLoc();
174 if (Result.Nodes.getNodeAs<Expr>(
"swapped-parameter")) {
175 const Expr *P0 = E->getArg(0);
176 const Expr *P1 = E->getArg(1);
177 diag(Loc,
"string constructor parameters are probably swapped;"
178 " expecting string(count, character)")
179 << tooling::fixit::createReplacement(*P0, *P1, Ctx)
180 << tooling::fixit::createReplacement(*P1, *P0, Ctx);
181 }
else if (Result.Nodes.getNodeAs<Expr>(
"empty-string")) {
182 diag(Loc,
"constructor creating an empty string");
183 }
else if (Result.Nodes.getNodeAs<Expr>(
"negative-length")) {
184 diag(Loc,
"negative value used as length parameter");
185 }
else if (Result.Nodes.getNodeAs<Expr>(
"negative-pos")) {
186 diag(Loc,
"negative value used as position of the "
187 "first character parameter");
188 }
else if (Result.Nodes.getNodeAs<Expr>(
"large-length")) {
189 if (WarnOnLargeLength)
190 diag(Loc,
"suspicious large length parameter");
191 }
else if (Result.Nodes.getNodeAs<Expr>(
"literal-with-length")) {
192 const auto *Str = Result.Nodes.getNodeAs<StringLiteral>(
"str");
193 const auto *Length = Result.Nodes.getNodeAs<IntegerLiteral>(
"length");
194 if (Length->getValue().ugt(Str->getLength())) {
195 diag(Loc,
"length is bigger than string literal size");
198 if (
const auto *Pos = Result.Nodes.getNodeAs<IntegerLiteral>(
"pos")) {
199 if (Pos->getValue().uge(Str->getLength())) {
200 diag(Loc,
"position of the first character parameter is bigger than "
201 "string literal character range");
202 }
else if (Length->getValue().ugt(
203 (Str->getLength() - Pos->getValue()).getZExtValue())) {
204 diag(Loc,
"length is bigger than remaining string literal size");
207 }
else if (
const auto *Ptr = Result.Nodes.getNodeAs<Expr>(
"from-ptr")) {
208 Expr::EvalResult ConstPtr;
209 if (!Ptr->isInstantiationDependent() &&
210 Ptr->EvaluateAsRValue(ConstPtr, Ctx) &&
211 ((ConstPtr.Val.isInt() && ConstPtr.Val.getInt().isZero()) ||
212 (ConstPtr.Val.isLValue() && ConstPtr.Val.isNullPointer()))) {
213 if (IsStringviewNullptrCheckEnabled &&
214 Result.Nodes.getNodeAs<CXXRecordDecl>(
"basic_string_view_decl")) {
219 diag(Loc,
"constructing string from nullptr is undefined behaviour");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.