brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.4 KiB · 8849c33 Raw
300 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 "UseScopedLockCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/Decl.h"12#include "clang/AST/Stmt.h"13#include "clang/AST/Type.h"14#include "clang/ASTMatchers/ASTMatchFinder.h"15#include "clang/ASTMatchers/ASTMatchers.h"16#include "clang/Basic/SourceLocation.h"17#include "clang/Lex/Lexer.h"18#include "llvm/ADT/SmallVector.h"19#include "llvm/ADT/Twine.h"20 21using namespace clang::ast_matchers;22 23namespace clang::tidy::modernize {24 25static bool isLockGuardDecl(const NamedDecl *Decl) {26  return Decl->getDeclName().isIdentifier() &&27         Decl->getName() == "lock_guard" && Decl->isInStdNamespace();28}29 30static bool isLockGuard(const QualType &Type) {31  if (const auto *Record = Type->getAsCanonical<RecordType>())32    if (const RecordDecl *Decl = Record->getDecl())33      return isLockGuardDecl(Decl);34 35  if (const auto *TemplateSpecType = Type->getAs<TemplateSpecializationType>())36    if (const TemplateDecl *Decl =37            TemplateSpecType->getTemplateName().getAsTemplateDecl())38      return isLockGuardDecl(Decl);39 40  return false;41}42 43static llvm::SmallVector<const VarDecl *>44getLockGuardsFromDecl(const DeclStmt *DS) {45  llvm::SmallVector<const VarDecl *> LockGuards;46 47  for (const Decl *Decl : DS->decls()) {48    if (const auto *VD = dyn_cast<VarDecl>(Decl)) {49      const QualType Type =50          VD->getType().getCanonicalType().getUnqualifiedType();51      if (isLockGuard(Type))52        LockGuards.push_back(VD);53    }54  }55 56  return LockGuards;57}58 59// Scans through the statements in a block and groups consecutive60// 'std::lock_guard' variable declarations together.61static llvm::SmallVector<llvm::SmallVector<const VarDecl *>>62findLocksInCompoundStmt(const CompoundStmt *Block,63                        const ast_matchers::MatchFinder::MatchResult &Result) {64  // store groups of consecutive 'std::lock_guard' declarations65  llvm::SmallVector<llvm::SmallVector<const VarDecl *>> LockGuardGroups;66  llvm::SmallVector<const VarDecl *> CurrentLockGuardGroup;67 68  auto AddAndClearCurrentGroup = [&]() {69    if (!CurrentLockGuardGroup.empty()) {70      LockGuardGroups.push_back(CurrentLockGuardGroup);71      CurrentLockGuardGroup.clear();72    }73  };74 75  for (const Stmt *Stmt : Block->body()) {76    if (const auto *DS = dyn_cast<DeclStmt>(Stmt)) {77      const llvm::SmallVector<const VarDecl *> LockGuards =78          getLockGuardsFromDecl(DS);79 80      if (!LockGuards.empty()) {81        CurrentLockGuardGroup.append(LockGuards);82        continue;83      }84    }85    AddAndClearCurrentGroup();86  }87 88  AddAndClearCurrentGroup();89 90  return LockGuardGroups;91}92 93// Find the exact source range of the 'lock_guard' token94static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) {95  const TypeLoc LockGuardTypeLoc = SourceInfo->getTypeLoc();96 97  return {LockGuardTypeLoc.getBeginLoc(), LockGuardTypeLoc.getEndLoc()};98}99 100// Find the exact source range of the 'lock_guard' name token101static SourceRange getLockGuardNameRange(const TypeSourceInfo *SourceInfo) {102  const auto TemplateLoc =103      SourceInfo->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();104  if (!TemplateLoc)105    return {};106 107  return {TemplateLoc.getTemplateNameLoc(),108          TemplateLoc.getLAngleLoc().getLocWithOffset(-1)};109}110 111const static StringRef UseScopedLockMessage =112    "use 'std::scoped_lock' instead of 'std::lock_guard'";113 114UseScopedLockCheck::UseScopedLockCheck(StringRef Name,115                                       ClangTidyContext *Context)116    : ClangTidyCheck(Name, Context),117      WarnOnSingleLocks(Options.get("WarnOnSingleLocks", true)),118      WarnOnUsingAndTypedef(Options.get("WarnOnUsingAndTypedef", true)) {}119 120void UseScopedLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {121  Options.store(Opts, "WarnOnSingleLocks", WarnOnSingleLocks);122  Options.store(Opts, "WarnOnUsingAndTypedef", WarnOnUsingAndTypedef);123}124 125void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {126  const auto LockGuardClassDecl =127      namedDecl(hasName("lock_guard"), isInStdNamespace());128 129  const auto LockGuardType =130      qualType(anyOf(hasUnqualifiedDesugaredType(131                         recordType(hasDeclaration(LockGuardClassDecl))),132                     hasUnqualifiedDesugaredType(templateSpecializationType(133                         hasDeclaration(LockGuardClassDecl)))));134 135  const auto LockVarDecl = varDecl(hasType(LockGuardType));136 137  if (WarnOnSingleLocks) {138    Finder->addMatcher(139        compoundStmt(140            unless(isExpansionInSystemHeader()),141            has(declStmt(has(LockVarDecl)).bind("lock-decl-single")),142            unless(has(declStmt(unless(equalsBoundNode("lock-decl-single")),143                                has(LockVarDecl))))),144        this);145  }146 147  Finder->addMatcher(148      compoundStmt(unless(isExpansionInSystemHeader()),149                   has(declStmt(has(LockVarDecl)).bind("lock-decl-multiple")),150                   has(declStmt(unless(equalsBoundNode("lock-decl-multiple")),151                                has(LockVarDecl))))152          .bind("block-multiple"),153      this);154 155  if (WarnOnUsingAndTypedef) {156    // Match 'typedef std::lock_guard<std::mutex> Lock'157    Finder->addMatcher(typedefDecl(unless(isExpansionInSystemHeader()),158                                   hasType(hasUnderlyingType(LockGuardType)))159                           .bind("lock-guard-typedef"),160                       this);161 162    // Match 'using Lock = std::lock_guard<std::mutex>'163    Finder->addMatcher(typeAliasDecl(unless(isExpansionInSystemHeader()),164                                     hasType(templateSpecializationType(165                                         hasDeclaration(LockGuardClassDecl))))166                           .bind("lock-guard-using-alias"),167                       this);168 169    // Match 'using std::lock_guard'170    Finder->addMatcher(171        usingDecl(unless(isExpansionInSystemHeader()),172                  hasAnyUsingShadowDecl(hasTargetDecl(LockGuardClassDecl)))173            .bind("lock-guard-using-decl"),174        this);175  }176}177 178void UseScopedLockCheck::check(const MatchFinder::MatchResult &Result) {179  if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("lock-decl-single")) {180    const llvm::SmallVector<const VarDecl *> Decls = getLockGuardsFromDecl(DS);181    diagOnMultipleLocks({Decls}, Result);182    return;183  }184 185  if (const auto *Compound =186          Result.Nodes.getNodeAs<CompoundStmt>("block-multiple")) {187    diagOnMultipleLocks(findLocksInCompoundStmt(Compound, Result), Result);188    return;189  }190 191  if (const auto *Typedef =192          Result.Nodes.getNodeAs<TypedefDecl>("lock-guard-typedef")) {193    diagOnSourceInfo(Typedef->getTypeSourceInfo(), Result);194    return;195  }196 197  if (const auto *UsingAlias =198          Result.Nodes.getNodeAs<TypeAliasDecl>("lock-guard-using-alias")) {199    diagOnSourceInfo(UsingAlias->getTypeSourceInfo(), Result);200    return;201  }202 203  if (const auto *Using =204          Result.Nodes.getNodeAs<UsingDecl>("lock-guard-using-decl")) {205    diagOnUsingDecl(Using, Result);206  }207}208 209void UseScopedLockCheck::diagOnSingleLock(210    const VarDecl *LockGuard, const MatchFinder::MatchResult &Result) {211  auto Diag = diag(LockGuard->getBeginLoc(), UseScopedLockMessage);212 213  const SourceRange LockGuardTypeRange =214      getLockGuardRange(LockGuard->getTypeSourceInfo());215 216  if (LockGuardTypeRange.isInvalid())217    return;218 219  // Create Fix-its only if we can find the constructor call to properly handle220  // 'std::lock_guard l(m, std::adopt_lock)' case.221  const auto *CtorCall =222      dyn_cast_if_present<CXXConstructExpr>(LockGuard->getInit());223  if (!CtorCall)224    return;225 226  if (CtorCall->getNumArgs() == 1) {227    Diag << FixItHint::CreateReplacement(LockGuardTypeRange,228                                         "std::scoped_lock");229    return;230  }231 232  if (CtorCall->getNumArgs() == 2) {233    const Expr *const *CtorArgs = CtorCall->getArgs();234 235    const Expr *MutexArg = CtorArgs[0];236    const Expr *AdoptLockArg = CtorArgs[1];237 238    const StringRef MutexSourceText = Lexer::getSourceText(239        CharSourceRange::getTokenRange(MutexArg->getSourceRange()),240        *Result.SourceManager, Result.Context->getLangOpts());241    const StringRef AdoptLockSourceText = Lexer::getSourceText(242        CharSourceRange::getTokenRange(AdoptLockArg->getSourceRange()),243        *Result.SourceManager, Result.Context->getLangOpts());244 245    Diag << FixItHint::CreateReplacement(LockGuardTypeRange, "std::scoped_lock")246         << FixItHint::CreateReplacement(247                SourceRange(MutexArg->getBeginLoc(), AdoptLockArg->getEndLoc()),248                (llvm::Twine(AdoptLockSourceText) + ", " + MutexSourceText)249                    .str());250    return;251  }252 253  llvm_unreachable("Invalid argument number of std::lock_guard constructor");254}255 256void UseScopedLockCheck::diagOnMultipleLocks(257    const llvm::SmallVector<llvm::SmallVector<const VarDecl *>> &LockGroups,258    const ast_matchers::MatchFinder::MatchResult &Result) {259  for (const llvm::SmallVector<const VarDecl *> &Group : LockGroups) {260    if (Group.size() == 1) {261      if (WarnOnSingleLocks)262        diagOnSingleLock(Group[0], Result);263    } else {264      diag(Group[0]->getBeginLoc(),265           "use single 'std::scoped_lock' instead of multiple "266           "'std::lock_guard'");267 268      for (const VarDecl *Lock : llvm::drop_begin(Group))269        diag(Lock->getLocation(), "additional 'std::lock_guard' declared here",270             DiagnosticIDs::Note);271    }272  }273}274 275void UseScopedLockCheck::diagOnSourceInfo(276    const TypeSourceInfo *LockGuardSourceInfo,277    const ast_matchers::MatchFinder::MatchResult &Result) {278  const TypeLoc TL = LockGuardSourceInfo->getTypeLoc();279 280  if (const auto TTL = TL.getAs<TemplateSpecializationTypeLoc>()) {281    auto Diag = diag(TTL.getBeginLoc(), UseScopedLockMessage);282 283    const SourceRange LockGuardRange =284        getLockGuardNameRange(LockGuardSourceInfo);285    if (LockGuardRange.isInvalid())286      return;287 288    Diag << FixItHint::CreateReplacement(LockGuardRange, "scoped_lock");289  }290}291 292void UseScopedLockCheck::diagOnUsingDecl(293    const UsingDecl *UsingDecl,294    const ast_matchers::MatchFinder::MatchResult &Result) {295  diag(UsingDecl->getLocation(), UseScopedLockMessage)296      << FixItHint::CreateReplacement(UsingDecl->getLocation(), "scoped_lock");297}298 299} // namespace clang::tidy::modernize300