brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.0 KiB · ac604b7 Raw
165 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ExplicitConstructorCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/ASTMatchers/ASTMatchers.h"13#include "clang/Lex/Lexer.h"14 15using namespace clang::ast_matchers;16 17namespace clang::tidy::google {18 19void ExplicitConstructorCheck::registerMatchers(MatchFinder *Finder) {20  Finder->addMatcher(21      cxxConstructorDecl(unless(anyOf(isImplicit(), // Compiler-generated.22                                      isDeleted(), isInstantiated())))23          .bind("ctor"),24      this);25  Finder->addMatcher(26      cxxConversionDecl(unless(anyOf(isExplicit(), // Already marked explicit.27                                     isImplicit(), // Compiler-generated.28                                     isDeleted(), isInstantiated())))29 30          .bind("conversion"),31      this);32}33 34// Looks for the token matching the predicate and returns the range of the found35// token including trailing whitespace.36static SourceRange findToken(const SourceManager &Sources,37                             const LangOptions &LangOpts,38                             SourceLocation StartLoc, SourceLocation EndLoc,39                             bool (*Pred)(const Token &)) {40  if (StartLoc.isMacroID() || EndLoc.isMacroID())41    return {};42  const FileID File = Sources.getFileID(Sources.getSpellingLoc(StartLoc));43  const StringRef Buf = Sources.getBufferData(File);44  const char *StartChar = Sources.getCharacterData(StartLoc);45  Lexer Lex(StartLoc, LangOpts, StartChar, StartChar, Buf.end());46  Lex.SetCommentRetentionState(true);47  Token Tok;48  do {49    Lex.LexFromRawLexer(Tok);50    if (Pred(Tok)) {51      Token NextTok;52      Lex.LexFromRawLexer(NextTok);53      return {Tok.getLocation(), NextTok.getLocation()};54    }55  } while (Tok.isNot(tok::eof) && Tok.getLocation() < EndLoc);56 57  return {};58}59 60static bool declIsStdInitializerList(const NamedDecl *D) {61  // First use the fast getName() method to avoid unnecessary calls to the62  // slow getQualifiedNameAsString().63  return D->getName() == "initializer_list" &&64         D->getQualifiedNameAsString() == "std::initializer_list";65}66 67static bool isStdInitializerList(QualType Type) {68  Type = Type.getCanonicalType();69  if (const auto *TS = Type->getAs<TemplateSpecializationType>()) {70    if (const TemplateDecl *TD = TS->getTemplateName().getAsTemplateDecl())71      return declIsStdInitializerList(TD);72  }73  if (const auto *RT = Type->getAs<RecordType>()) {74    if (const auto *Specialization =75            dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl()))76      return declIsStdInitializerList(Specialization->getSpecializedTemplate());77  }78  return false;79}80 81void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {82  constexpr char NoExpressionWarningMessage[] =83      "%0 must be marked explicit to avoid unintentional implicit conversions";84  constexpr char WithExpressionWarningMessage[] =85      "%0 explicit expression evaluates to 'false'";86 87  if (const auto *Conversion =88          Result.Nodes.getNodeAs<CXXConversionDecl>("conversion")) {89    if (Conversion->isOutOfLine())90      return;91    const SourceLocation Loc = Conversion->getLocation();92    // Ignore all macros until we learn to ignore specific ones (e.g. used in93    // gmock to define matchers).94    if (Loc.isMacroID())95      return;96    diag(Loc, NoExpressionWarningMessage)97        << Conversion << FixItHint::CreateInsertion(Loc, "explicit ");98    return;99  }100 101  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");102  if (Ctor->isOutOfLine() || Ctor->getNumParams() == 0 ||103      Ctor->getMinRequiredArguments() > 1)104    return;105 106  const ExplicitSpecifier ExplicitSpec = Ctor->getExplicitSpecifier();107 108  const bool TakesInitializerList = isStdInitializerList(109      Ctor->getParamDecl(0)->getType().getNonReferenceType());110  if (ExplicitSpec.isExplicit() &&111      (Ctor->isCopyOrMoveConstructor() || TakesInitializerList)) {112    auto IsKwExplicit = [](const Token &Tok) {113      return Tok.is(tok::raw_identifier) &&114             Tok.getRawIdentifier() == "explicit";115    };116    const SourceRange ExplicitTokenRange =117        findToken(*Result.SourceManager, getLangOpts(),118                  Ctor->getOuterLocStart(), Ctor->getEndLoc(), IsKwExplicit);119    StringRef ConstructorDescription;120    if (Ctor->isMoveConstructor())121      ConstructorDescription = "move";122    else if (Ctor->isCopyConstructor())123      ConstructorDescription = "copy";124    else125      ConstructorDescription = "initializer-list";126 127    auto Diag = diag(Ctor->getLocation(),128                     "%0 constructor should not be declared explicit")129                << ConstructorDescription;130    if (ExplicitTokenRange.isValid()) {131      Diag << FixItHint::CreateRemoval(132          CharSourceRange::getCharRange(ExplicitTokenRange));133    }134    return;135  }136 137  if (ExplicitSpec.isExplicit() || Ctor->isCopyOrMoveConstructor() ||138      TakesInitializerList)139    return;140 141  // Don't complain about explicit(false) or dependent expressions142  const Expr *ExplicitExpr = ExplicitSpec.getExpr();143  if (ExplicitExpr) {144    ExplicitExpr = ExplicitExpr->IgnoreImplicit();145    if (isa<CXXBoolLiteralExpr>(ExplicitExpr) ||146        ExplicitExpr->isInstantiationDependent())147      return;148  }149 150  const bool SingleArgument =151      Ctor->getNumParams() == 1 && !Ctor->getParamDecl(0)->isParameterPack();152  const SourceLocation Loc = Ctor->getLocation();153  auto Diag =154      diag(Loc, ExplicitExpr ? WithExpressionWarningMessage155                             : NoExpressionWarningMessage)156      << (SingleArgument157              ? "single-argument constructors"158              : "constructors that are callable with a single argument");159 160  if (!ExplicitExpr)161    Diag << FixItHint::CreateInsertion(Loc, "explicit ");162}163 164} // namespace clang::tidy::google165