================ @@ -0,0 +1,160 @@ +//===----------------------------------------------------------------------===// +// +// 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 "UseBracedInitializationCheck.h" +#include "../utils/LexerUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::misc { + +namespace { + +AST_MATCHER_P(VarDecl, hasInitStyle, VarDecl::InitializationStyle, Style) { + return Node.getInitStyle() == Style; +} + +AST_MATCHER(Type, isDependentType) { return Node.isDependentType(); } + +AST_MATCHER(CXXConstructExpr, noMacroParens) { + const SourceRange Range = Node.getParenOrBraceRange(); + return Range.isValid() && !Range.getBegin().isMacroID() && + !Range.getEnd().isMacroID(); +} + +AST_MATCHER(Expr, isCXXParenListInitExpr) { + return isa<CXXParenListInitExpr>(Node); +} + +/// Matches CXXConstructExpr whose target class has any constructor +/// taking 'std::initializer_list'. When such a constructor exists, braced +/// initialization may call it instead of the intended constructor. +AST_MATCHER(CXXConstructExpr, constructsTypeWithInitListCtor) { + const CXXRecordDecl *RD = Node.getConstructor()->getParent(); + if (!RD || !RD->hasDefinition()) + return false; + return llvm::any_of(RD->ctors(), [](const CXXConstructorDecl *Ctor) { + if (Ctor->getNumParams() == 0) + return false; + const QualType FirstParam = + Ctor->getParamDecl(0)->getType().getNonReferenceType(); + const auto *Record = FirstParam->getAsCXXRecordDecl(); + if (!Record || !Record->getDeclName().isIdentifier() || + Record->getName() != "initializer_list" || !Record->isInStdNamespace()) + return false; + // [dcl.init.list] p2: all other params must have defaults. + for (unsigned I = 1; I < Ctor->getNumParams(); ++I) + if (!Ctor->getParamDecl(I)->hasDefaultArg()) + return false; + return true; + }); +} + +} // namespace + +void UseBracedInitializationCheck::registerMatchers(MatchFinder *Finder) { + auto GoodCtor = allOf( + noMacroParens(), unless(constructsTypeWithInitListCtor()), + unless(isInTemplateInstantiation()), unless(isListInitialization())); + auto GoodCtorExpr = cxxConstructExpr(GoodCtor).bind("ctor"); + auto GoodVar = + allOf(unless(hasType(isDependentType())), unless(hasType(autoType()))); + + // Variable declarations: Simple w(1), Takes t({1, 2}) + Finder->addMatcher(varDecl(hasInitStyle(VarDecl::CallInit), + hasInitializer(ignoringImplicit(GoodCtorExpr)), + GoodVar), + this); + + // Scalar direct-init: int x(42), double d(3.14) + Finder->addMatcher( + varDecl(hasInitStyle(VarDecl::CallInit), + hasInitializer(unless(ignoringImplicit(cxxConstructExpr()))), + GoodVar) + .bind("scalar"), + this); + + Finder->addMatcher(cxxFunctionalCastExpr(has(GoodCtorExpr)), this); + Finder->addMatcher(cxxTemporaryObjectExpr(GoodCtor).bind("ctor"), this); + Finder->addMatcher(cxxNewExpr(has(GoodCtorExpr)), this); ---------------- vbvictor wrote:
Probably worth to make an option for each of this matcher.. C++ core guildelines only suggest it for variable declarations: ``` Flag uses of () initialization syntax that are actually declarations. (Many compilers should warn on this already.) ``` https://github.com/llvm/llvm-project/pull/184009 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
