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 }