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 anyOf(argumentCountIs(2), argumentCountIs(3)),
85 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(
105 anyOf(argumentCountIs(2),
106 allOf(argumentCountIs(3),
107 hasArgument(2, unless(hasType(qualType(isInteger())))))),
108 hasArgument(0, hasType(CharPtrType)),
109 hasArgument(1, hasType(isInteger())),
112 hasArgument(1, ZeroExpr.bind(
"empty-string")),
114 hasArgument(1, NegativeExpr.bind(
"negative-length")),
116 hasArgument(1, LargeLengthExpr.bind(
"large-length")),
118 allOf(hasArgument(0, ConstStrLiteral.bind(
"literal-with-length")),
119 hasArgument(1, ignoringParenImpCasts(
120 integerLiteral().bind(
"length"))))))
121 .bind(
"constructor"),
128 hasDeclaration(cxxConstructorDecl(ofClass(
130 anyOf(argumentCountIs(3), argumentCountIs(4)),
131 hasArgument(0, hasType(CharPtrType)),
132 hasArgument(1, hasType(qualType(isInteger()))),
133 hasArgument(2, hasType(qualType(isInteger()))),
136 hasArgument(2, ZeroExpr.bind(
"empty-string")),
138 hasArgument(1, NegativeExpr.bind(
"negative-pos")),
140 hasArgument(2, NegativeExpr.bind(
"negative-length")),
142 hasArgument(2, LargeLengthExpr.bind(
"large-length")),
144 allOf(hasArgument(0, ConstStrLiteral.bind(
"literal-with-length")),
146 1, ignoringParenImpCasts(integerLiteral().bind(
"pos"))),
147 hasArgument(2, ignoringParenImpCasts(
148 integerLiteral().bind(
"length"))))))
149 .bind(
"constructor"),
158 hasDeclaration(cxxConstructorDecl(ofClass(anyOf(
159 cxxRecordDecl(hasName(
"basic_string_view"))
160 .bind(
"basic_string_view_decl"),
162 hasArgument(0, expr().bind(
"from-ptr")),
166 anyOf(hasArgument(1, unless(hasType(isInteger()))),
168 .bind(
"constructor")),
173 const ASTContext &Ctx = *Result.Context;
174 const auto *E = Result.Nodes.getNodeAs<CXXConstructExpr>(
"constructor");
175 assert(E &&
"missing constructor expression");
176 const SourceLocation Loc = E->getBeginLoc();
178 if (Result.Nodes.getNodeAs<Expr>(
"swapped-parameter")) {
179 const Expr *P0 = E->getArg(0);
180 const Expr *P1 = E->getArg(1);
181 diag(Loc,
"string constructor parameters are probably swapped;"
182 " expecting string(count, character)")
183 << tooling::fixit::createReplacement(*P0, *P1, Ctx)
184 << tooling::fixit::createReplacement(*P1, *P0, Ctx);
185 }
else if (Result.Nodes.getNodeAs<Expr>(
"empty-string")) {
186 diag(Loc,
"constructor creating an empty string");
187 }
else if (Result.Nodes.getNodeAs<Expr>(
"negative-length")) {
188 diag(Loc,
"negative value used as length parameter");
189 }
else if (Result.Nodes.getNodeAs<Expr>(
"negative-pos")) {
190 diag(Loc,
"negative value used as position of the "
191 "first character parameter");
192 }
else if (Result.Nodes.getNodeAs<Expr>(
"large-length")) {
193 if (WarnOnLargeLength)
194 diag(Loc,
"suspicious large length parameter");
195 }
else if (Result.Nodes.getNodeAs<Expr>(
"literal-with-length")) {
196 const auto *Str = Result.Nodes.getNodeAs<StringLiteral>(
"str");
197 const auto *Length = Result.Nodes.getNodeAs<IntegerLiteral>(
"length");
198 if (Length->getValue().ugt(Str->getLength())) {
199 diag(Loc,
"length is bigger than string literal size");
202 if (
const auto *Pos = Result.Nodes.getNodeAs<IntegerLiteral>(
"pos")) {
203 if (Pos->getValue().uge(Str->getLength())) {
204 diag(Loc,
"position of the first character parameter is bigger than "
205 "string literal character range");
206 }
else if (Length->getValue().ugt(
207 (Str->getLength() - Pos->getValue()).getZExtValue())) {
208 diag(Loc,
"length is bigger than remaining string literal size");
211 }
else if (
const auto *Ptr = Result.Nodes.getNodeAs<Expr>(
"from-ptr")) {
212 Expr::EvalResult ConstPtr;
213 if (!Ptr->isInstantiationDependent() &&
214 Ptr->EvaluateAsRValue(ConstPtr, Ctx) &&
215 ((ConstPtr.Val.isInt() && ConstPtr.Val.getInt().isZero()) ||
216 (ConstPtr.Val.isLValue() && ConstPtr.Val.isNullPointer()))) {
217 if (IsStringviewNullptrCheckEnabled &&
218 Result.Nodes.getNodeAs<CXXRecordDecl>(
"basic_string_view_decl")) {
223 diag(Loc,
"constructing string from nullptr is undefined behaviour");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.