void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) { const CXXConstructorDecl *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor"); // Do not be confused: isExplicit means 'explicit' keyword is present, // isImplicit means that it's a compiler-generated constructor. if (Ctor->isOutOfLine() || Ctor->isImplicit() || Ctor->isDeleted() || Ctor->getNumParams() == 0 || Ctor->getMinRequiredArguments() > 1) return; bool takesInitializerList = isStdInitializerList( Ctor->getParamDecl(0)->getType().getNonReferenceType()); if (Ctor->isExplicit() && (Ctor->isCopyOrMoveConstructor() || takesInitializerList)) { auto isKWExplicit = [](const Token &Tok) { return Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() == "explicit"; }; SourceRange ExplicitTokenRange = FindToken(*Result.SourceManager, Result.Context->getLangOpts(), Ctor->getOuterLocStart(), Ctor->getLocEnd(), isKWExplicit); StringRef ConstructorDescription; if (Ctor->isMoveConstructor()) ConstructorDescription = "move"; else if (Ctor->isCopyConstructor()) ConstructorDescription = "copy"; else ConstructorDescription = "initializer-list"; DiagnosticBuilder Diag = diag(Ctor->getLocation(), "%0 constructor should not be declared explicit") << ConstructorDescription; if (ExplicitTokenRange.isValid()) { Diag << FixItHint::CreateRemoval( CharSourceRange::getCharRange(ExplicitTokenRange)); } return; } if (Ctor->isExplicit() || Ctor->isCopyOrMoveConstructor() || takesInitializerList) return; bool SingleArgument = Ctor->getNumParams() == 1 && !Ctor->getParamDecl(0)->isParameterPack(); SourceLocation Loc = Ctor->getLocation(); diag(Loc, "%0 must be marked explicit to avoid unintentional implicit conversions") << (SingleArgument ? "single-argument constructors" : "constructors that are callable with a single argument") << FixItHint::CreateInsertion(Loc, "explicit "); }
void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) { constexpr char WarningMessage[] = "%0 must be marked explicit to avoid unintentional implicit conversions"; if (const auto *Conversion = Result.Nodes.getNodeAs<CXXConversionDecl>("conversion")) { if (Conversion->isOutOfLine()) return; SourceLocation Loc = Conversion->getLocation(); // Ignore all macros until we learn to ignore specific ones (e.g. used in // gmock to define matchers). if (Loc.isMacroID()) return; diag(Loc, WarningMessage) << Conversion << FixItHint::CreateInsertion(Loc, "explicit "); return; } const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor"); if (Ctor->isOutOfLine() || Ctor->getNumParams() == 0 || Ctor->getMinRequiredArguments() > 1) return; bool takesInitializerList = isStdInitializerList( Ctor->getParamDecl(0)->getType().getNonReferenceType()); if (Ctor->isExplicit() && (Ctor->isCopyOrMoveConstructor() || takesInitializerList)) { auto isKWExplicit = [](const Token &Tok) { return Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() == "explicit"; }; SourceRange ExplicitTokenRange = FindToken(*Result.SourceManager, getLangOpts(), Ctor->getOuterLocStart(), Ctor->getEndLoc(), isKWExplicit); StringRef ConstructorDescription; if (Ctor->isMoveConstructor()) ConstructorDescription = "move"; else if (Ctor->isCopyConstructor()) ConstructorDescription = "copy"; else ConstructorDescription = "initializer-list"; auto Diag = diag(Ctor->getLocation(), "%0 constructor should not be declared explicit") << ConstructorDescription; if (ExplicitTokenRange.isValid()) { Diag << FixItHint::CreateRemoval( CharSourceRange::getCharRange(ExplicitTokenRange)); } return; } if (Ctor->isExplicit() || Ctor->isCopyOrMoveConstructor() || takesInitializerList) return; bool SingleArgument = Ctor->getNumParams() == 1 && !Ctor->getParamDecl(0)->isParameterPack(); SourceLocation Loc = Ctor->getLocation(); diag(Loc, WarningMessage) << (SingleArgument ? "single-argument constructors" : "constructors that are callable with a single argument") << FixItHint::CreateInsertion(Loc, "explicit "); }