|
14 | 14 | #include "clang/ASTMatchers/ASTMatchFinder.h"
|
15 | 15 | #include "clang/Lex/Lexer.h"
|
16 | 16 | #include "clang/Lex/Preprocessor.h"
|
| 17 | +#include "llvm/ADT/APSInt.h" |
17 | 18 | #include "llvm/ADT/SmallVector.h"
|
18 | 19 | #include <memory>
|
19 | 20 | #include <optional>
|
@@ -330,6 +331,73 @@ isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) {
|
330 | 331 | // FIXME: Handle loop bodies.
|
331 | 332 | return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse));
|
332 | 333 | }
|
| 334 | + |
| 335 | +// Given a two-param std::span construct call, matches iff the call has the |
| 336 | +// following forms: |
| 337 | +// 1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE |
| 338 | +// 2. `std::span<T>{new T, 1}` |
| 339 | +// 3. `std::span<T>{&var, 1}` |
| 340 | +// 4. `std::span<T>{a, n}`, where `a` is of an array-of-T with constant size |
| 341 | +// `n` |
| 342 | +// 5. `std::span<T>{any, 0}` |
| 343 | +AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) { |
| 344 | + assert(Node.getNumArgs() == 2 && |
| 345 | + "expecting a two-parameter std::span constructor"); |
| 346 | + const Expr *Arg0 = Node.getArg(0)->IgnoreImplicit(); |
| 347 | + const Expr *Arg1 = Node.getArg(1)->IgnoreImplicit(); |
| 348 | + auto HaveEqualConstantValues = [&Finder](const Expr *E0, const Expr *E1) { |
| 349 | + if (auto E0CV = E0->getIntegerConstantExpr(Finder->getASTContext())) |
| 350 | + if (auto E1CV = E1->getIntegerConstantExpr(Finder->getASTContext())) { |
| 351 | + return APSInt::compareValues(*E0CV, *E1CV) == 0; |
| 352 | + } |
| 353 | + return false; |
| 354 | + }; |
| 355 | + auto AreSameDRE = [](const Expr *E0, const Expr *E1) { |
| 356 | + if (auto *DRE0 = dyn_cast<DeclRefExpr>(E0)) |
| 357 | + if (auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) { |
| 358 | + return DRE0->getDecl() == DRE1->getDecl(); |
| 359 | + } |
| 360 | + return false; |
| 361 | + }; |
| 362 | + std::optional<APSInt> Arg1CV = |
| 363 | + Arg1->getIntegerConstantExpr(Finder->getASTContext()); |
| 364 | + |
| 365 | + if (Arg1CV && Arg1CV->isZero()) |
| 366 | + // Check form 5: |
| 367 | + return true; |
| 368 | + switch (Arg0->IgnoreImplicit()->getStmtClass()) { |
| 369 | + case Stmt::CXXNewExprClass: |
| 370 | + if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize()) { |
| 371 | + // Check form 1: |
| 372 | + return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) || |
| 373 | + HaveEqualConstantValues(*Size, Arg1); |
| 374 | + } |
| 375 | + // TODO: what's placeholder type? avoid it for now. |
| 376 | + if (!cast<CXXNewExpr>(Arg0)->hasPlaceholderType()) { |
| 377 | + // Check form 2: |
| 378 | + return Arg1CV && Arg1CV->isOne(); |
| 379 | + } |
| 380 | + break; |
| 381 | + case Stmt::UnaryOperatorClass: |
| 382 | + if (cast<UnaryOperator>(Arg0)->getOpcode() == |
| 383 | + UnaryOperator::Opcode::UO_AddrOf) |
| 384 | + // Check form 3: |
| 385 | + return Arg1CV && Arg1CV->isOne(); |
| 386 | + break; |
| 387 | + default: |
| 388 | + break; |
| 389 | + } |
| 390 | + |
| 391 | + QualType Arg0Ty = Arg0->IgnoreImplicit()->getType(); |
| 392 | + |
| 393 | + if (Arg0Ty->isConstantArrayType()) { |
| 394 | + const APInt &ConstArrSize = cast<ConstantArrayType>(Arg0Ty)->getSize(); |
| 395 | + |
| 396 | + // Check form 4: |
| 397 | + return Arg1CV && APSInt::compareValues(APSInt(ConstArrSize), *Arg1CV) == 0; |
| 398 | + } |
| 399 | + return false; |
| 400 | +} |
333 | 401 | } // namespace clang::ast_matchers
|
334 | 402 |
|
335 | 403 | namespace {
|
@@ -619,7 +687,8 @@ class SpanTwoParamConstructorGadget : public WarningGadget {
|
619 | 687 | cxxConstructorDecl(hasDeclContext(isInStdNamespace()), hasName("span"),
|
620 | 688 | parameterCountIs(2)));
|
621 | 689 |
|
622 |
| - return stmt(cxxConstructExpr(HasTwoParamSpanCtorDecl) |
| 690 | + return stmt(cxxConstructExpr(HasTwoParamSpanCtorDecl, |
| 691 | + unless(isSafeSpanTwoParamConstruct())) |
623 | 692 | .bind(SpanTwoParamConstructorTag));
|
624 | 693 | }
|
625 | 694 |
|
|
0 commit comments