tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

NonStdMoveChecker.cpp (4534B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "NonStdMoveChecker.h"
      6 #include "CustomMatchers.h"
      7 #include "clang/Lex/Lexer.h"
      8 
      9 constexpr const char *kConstructExpr = "construct";
     10 constexpr const char *kOperatorCallExpr = "operator-call";
     11 constexpr const char *kSourceExpr = "source-expr";
     12 constexpr const char *kMaterializeExpr = "materialize-expr";
     13 
     14 void NonStdMoveChecker::registerMatchers(MatchFinder *AstMatcher) {
     15 
     16  // Assignment through forget
     17  AstMatcher->addMatcher(
     18      cxxOperatorCallExpr(
     19          hasOverloadedOperatorName("="),
     20          hasAnyArgument(materializeTemporaryExpr(
     21                             has(cxxBindTemporaryExpr(has(cxxMemberCallExpr(
     22                                 has(memberExpr(member(hasName("forget")))),
     23                                 on(expr().bind(kSourceExpr)))))))
     24                             .bind(kMaterializeExpr)))
     25          .bind(kOperatorCallExpr),
     26      this);
     27 
     28  // Construction through forget
     29 
     30  AstMatcher->addMatcher(
     31      cxxConstructExpr(has(materializeTemporaryExpr(
     32                               has(cxxBindTemporaryExpr(has(cxxMemberCallExpr(
     33                                   has(memberExpr(member(hasName("forget")))),
     34                                   on(expr().bind(kSourceExpr)))))))
     35                               .bind(kMaterializeExpr)))
     36          .bind(kConstructExpr),
     37      this);
     38 }
     39 
     40 #if CLANG_VERSION_FULL >= 1600
     41 std::optional<FixItHint>
     42 #else
     43 Optional<FixItHint>
     44 #endif
     45 NonStdMoveChecker::makeFixItHint(const MatchFinder::MatchResult &Result,
     46                                 const Expr *const TargetExpr) {
     47  const auto *MaterializeExpr = Result.Nodes.getNodeAs<Expr>(kMaterializeExpr);
     48 
     49  // TODO: In principle, we should check here if TargetExpr if
     50  // assignable/constructible from std::move(SourceExpr). Not sure how to do
     51  // this. Currently, we only filter out the case where the targetTypeTemplate
     52  // is already_AddRefed, where this is known to fail.
     53 
     54  const auto *targetTypeTemplate = getNonTemplateSpecializedCXXRecordDecl(
     55      TargetExpr->getType().getCanonicalType());
     56  const auto *sourceTypeTemplate = getNonTemplateSpecializedCXXRecordDecl(
     57      MaterializeExpr->getType().getCanonicalType());
     58 
     59  if (targetTypeTemplate && sourceTypeTemplate) {
     60    // TODO is there a better way to check this than by name? otherwise, the
     61    // names probably are necessarily unique in the scope
     62    if (targetTypeTemplate->getName() == sourceTypeTemplate->getName() &&
     63        targetTypeTemplate->getName() == "already_AddRefed") {
     64      return {};
     65    }
     66  }
     67 
     68  const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>(kSourceExpr);
     69 
     70  const auto sourceText = Lexer::getSourceText(
     71      CharSourceRange::getTokenRange(SourceExpr->getSourceRange()),
     72      Result.Context->getSourceManager(), Result.Context->getLangOpts());
     73 
     74  return FixItHint::CreateReplacement(MaterializeExpr->getSourceRange(),
     75                                      ("std::move(" + sourceText + ")").str());
     76 }
     77 
     78 void NonStdMoveChecker::check(const MatchFinder::MatchResult &Result) {
     79  // TODO: Include source and target type name in messages.
     80 
     81  const auto *OCE =
     82      Result.Nodes.getNodeAs<CXXOperatorCallExpr>(kOperatorCallExpr);
     83 
     84  if (OCE) {
     85    const auto *refPtrDecl =
     86        dyn_cast<const CXXRecordDecl>(OCE->getCalleeDecl()->getDeclContext());
     87 
     88    const auto XFixItHint = makeFixItHint(Result, OCE);
     89    // TODO: produce diagnostic but no FixItHint in this case?
     90    if (XFixItHint) {
     91      diag(OCE->getBeginLoc(), "non-standard move assignment to %0 obscures "
     92                               "move, use std::move instead")
     93          << refPtrDecl << *XFixItHint;
     94    }
     95  }
     96 
     97  const auto *CoE = Result.Nodes.getNodeAs<CXXConstructExpr>(kConstructExpr);
     98 
     99  if (CoE) {
    100    const auto *refPtrDecl =
    101        dyn_cast<const CXXRecordDecl>(CoE->getConstructor()->getDeclContext());
    102 
    103    const auto XFixItHint = makeFixItHint(Result, CoE);
    104    // TODO: produce diagnostic but no FixItHint in this case?
    105    if (XFixItHint) {
    106      diag(CoE->getBeginLoc(), "non-standard move construction of %0 obscures "
    107                               "move, use std::move instead")
    108          << refPtrDecl << *XFixItHint;
    109    }
    110  }
    111 
    112  // TODO: What about swap calls immediately after default-construction? These
    113  // can also be replaced by move-construction, but this may require
    114  // control-flow analysis.
    115 }