| //===--- ProTypeVarargCheck.cpp - clang-tidy-------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ProTypeVarargCheck.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| #include "clang/ASTMatchers/ASTMatchers.h" |
| #include "clang/Basic/TargetInfo.h" |
| |
| using namespace clang::ast_matchers; |
| |
| namespace clang { |
| namespace tidy { |
| namespace cppcoreguidelines { |
| |
| const internal::VariadicDynCastAllOfMatcher<Stmt, VAArgExpr> VAArgExpr; |
| |
| static constexpr StringRef AllowedVariadics[] = { |
| // clang-format off |
| "__builtin_isgreater", |
| "__builtin_isgreaterequal", |
| "__builtin_isless", |
| "__builtin_islessequal", |
| "__builtin_islessgreater", |
| "__builtin_isunordered", |
| "__builtin_fpclassify", |
| "__builtin_isfinite", |
| "__builtin_isinf", |
| "__builtin_isinf_sign", |
| "__builtin_isnan", |
| "__builtin_isnormal", |
| "__builtin_signbit", |
| "__builtin_constant_p", |
| "__builtin_classify_type", |
| "__builtin_va_start", |
| "__builtin_assume_aligned", // Documented as variadic to support default |
| // parameters. |
| "__builtin_prefetch", // Documented as variadic to support default |
| // parameters. |
| "__builtin_shufflevector", // Documented as variadic but with a defined |
| // number of args based on vector size. |
| "__builtin_convertvector", |
| "__builtin_call_with_static_chain", |
| "__builtin_annotation", |
| "__builtin_add_overflow", |
| "__builtin_sub_overflow", |
| "__builtin_mul_overflow", |
| "__builtin_preserve_access_index", |
| "__builtin_nontemporal_store", |
| "__builtin_nontemporal_load", |
| "__builtin_ms_va_start", |
| // clang-format on |
| }; |
| |
| namespace { |
| AST_MATCHER(QualType, isVAList) { |
| ASTContext &Context = Finder->getASTContext(); |
| QualType Desugar = Node.getDesugaredType(Context); |
| QualType NodeTy = Node.getUnqualifiedType(); |
| |
| auto CheckVaList = [](QualType NodeTy, QualType Expected, |
| const ASTContext &Context) { |
| if (NodeTy == Expected) |
| return true; |
| QualType Desugar = NodeTy; |
| QualType Ty; |
| do { |
| Ty = Desugar; |
| Desugar = Ty.getSingleStepDesugaredType(Context); |
| if (Desugar == Expected) |
| return true; |
| } while (Desugar != Ty); |
| return false; |
| }; |
| |
| // The internal implementation of __builtin_va_list depends on the target |
| // type. Some targets implements va_list as 'char *' or 'void *'. |
| // In these cases we need to remove all typedefs one by one to check this. |
| using BuiltinVaListKind = TargetInfo::BuiltinVaListKind; |
| BuiltinVaListKind VaListKind = Context.getTargetInfo().getBuiltinVaListKind(); |
| if (VaListKind == BuiltinVaListKind::CharPtrBuiltinVaList || |
| VaListKind == BuiltinVaListKind::VoidPtrBuiltinVaList) { |
| if (CheckVaList(NodeTy, Context.getBuiltinVaListType(), Context)) |
| return true; |
| } else if (Desugar == |
| Context.getBuiltinVaListType().getDesugaredType(Context)) { |
| return true; |
| } |
| |
| // We also need to check the implementation of __builtin_ms_va_list in the |
| // same way, because it may differ from the va_list implementation. |
| if (Desugar == Context.getBuiltinMSVaListType().getDesugaredType(Context) && |
| CheckVaList(NodeTy, Context.getBuiltinMSVaListType(), Context)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| AST_MATCHER_P(AdjustedType, hasOriginalType, |
| ast_matchers::internal::Matcher<QualType>, InnerType) { |
| return InnerType.matches(Node.getOriginalType(), Finder, Builder); |
| } |
| } // namespace |
| |
| void ProTypeVarargCheck::registerMatchers(MatchFinder *Finder) { |
| Finder->addMatcher(VAArgExpr().bind("va_use"), this); |
| |
| Finder->addMatcher( |
| callExpr(callee(functionDecl(isVariadic(), |
| unless(hasAnyName(AllowedVariadics))))) |
| .bind("callvararg"), |
| this); |
| |
| Finder->addMatcher( |
| varDecl(unless(parmVarDecl()), |
| hasType(qualType( |
| anyOf(isVAList(), decayedType(hasOriginalType(isVAList())))))) |
| .bind("va_list"), |
| this); |
| } |
| |
| static bool hasSingleVariadicArgumentWithValue(const CallExpr *C, uint64_t I) { |
| const auto *FDecl = dyn_cast<FunctionDecl>(C->getCalleeDecl()); |
| if (!FDecl) |
| return false; |
| |
| auto N = FDecl->getNumParams(); // Number of parameters without '...' |
| if (C->getNumArgs() != N + 1) |
| return false; // more/less than one argument passed to '...' |
| |
| const auto *IntLit = |
| dyn_cast<IntegerLiteral>(C->getArg(N)->IgnoreParenImpCasts()); |
| if (!IntLit) |
| return false; |
| |
| if (IntLit->getValue() != I) |
| return false; |
| |
| return true; |
| } |
| |
| void ProTypeVarargCheck::check(const MatchFinder::MatchResult &Result) { |
| if (const auto *Matched = Result.Nodes.getNodeAs<CallExpr>("callvararg")) { |
| if (hasSingleVariadicArgumentWithValue(Matched, 0)) |
| return; |
| diag(Matched->getExprLoc(), "do not call c-style vararg functions"); |
| } |
| |
| if (const auto *Matched = Result.Nodes.getNodeAs<Expr>("va_use")) { |
| diag(Matched->getExprLoc(), |
| "do not use va_arg to define c-style vararg functions; " |
| "use variadic templates instead"); |
| } |
| |
| if (const auto *Matched = Result.Nodes.getNodeAs<VarDecl>("va_list")) { |
| auto SR = Matched->getSourceRange(); |
| if (SR.isInvalid()) |
| return; // some implicitly generated builtins take va_list |
| diag(SR.getBegin(), "do not declare variables of type va_list; " |
| "use variadic templates instead"); |
| } |
| } |
| |
| } // namespace cppcoreguidelines |
| } // namespace tidy |
| } // namespace clang |