summaryrefslogtreecommitdiffstats
path: root/build/clang-plugin/alpha/NonStdMoveChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'build/clang-plugin/alpha/NonStdMoveChecker.cpp')
-rw-r--r--build/clang-plugin/alpha/NonStdMoveChecker.cpp115
1 files changed, 115 insertions, 0 deletions
diff --git a/build/clang-plugin/alpha/NonStdMoveChecker.cpp b/build/clang-plugin/alpha/NonStdMoveChecker.cpp
new file mode 100644
index 0000000000..e9ede00cea
--- /dev/null
+++ b/build/clang-plugin/alpha/NonStdMoveChecker.cpp
@@ -0,0 +1,115 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "NonStdMoveChecker.h"
+#include "CustomMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+constexpr const char *kConstructExpr = "construct";
+constexpr const char *kOperatorCallExpr = "operator-call";
+constexpr const char *kSourceExpr = "source-expr";
+constexpr const char *kMaterializeExpr = "materialize-expr";
+
+void NonStdMoveChecker::registerMatchers(MatchFinder *AstMatcher) {
+
+ // Assignment through forget
+ AstMatcher->addMatcher(
+ cxxOperatorCallExpr(
+ hasOverloadedOperatorName("="),
+ hasAnyArgument(materializeTemporaryExpr(
+ has(cxxBindTemporaryExpr(has(cxxMemberCallExpr(
+ has(memberExpr(member(hasName("forget")))),
+ on(expr().bind(kSourceExpr)))))))
+ .bind(kMaterializeExpr)))
+ .bind(kOperatorCallExpr),
+ this);
+
+ // Construction through forget
+
+ AstMatcher->addMatcher(
+ cxxConstructExpr(has(materializeTemporaryExpr(
+ has(cxxBindTemporaryExpr(has(cxxMemberCallExpr(
+ has(memberExpr(member(hasName("forget")))),
+ on(expr().bind(kSourceExpr)))))))
+ .bind(kMaterializeExpr)))
+ .bind(kConstructExpr),
+ this);
+}
+
+#if CLANG_VERSION_FULL >= 1600
+std::optional<FixItHint>
+#else
+Optional<FixItHint>
+#endif
+NonStdMoveChecker::makeFixItHint(const MatchFinder::MatchResult &Result,
+ const Expr *const TargetExpr) {
+ const auto *MaterializeExpr = Result.Nodes.getNodeAs<Expr>(kMaterializeExpr);
+
+ // TODO: In principle, we should check here if TargetExpr if
+ // assignable/constructible from std::move(SourceExpr). Not sure how to do
+ // this. Currently, we only filter out the case where the targetTypeTemplate
+ // is already_AddRefed, where this is known to fail.
+
+ const auto *targetTypeTemplate = getNonTemplateSpecializedCXXRecordDecl(
+ TargetExpr->getType().getCanonicalType());
+ const auto *sourceTypeTemplate = getNonTemplateSpecializedCXXRecordDecl(
+ MaterializeExpr->getType().getCanonicalType());
+
+ if (targetTypeTemplate && sourceTypeTemplate) {
+ // TODO is there a better way to check this than by name? otherwise, the
+ // names probably are necessarily unique in the scope
+ if (targetTypeTemplate->getName() == sourceTypeTemplate->getName() &&
+ targetTypeTemplate->getName() == "already_AddRefed") {
+ return {};
+ }
+ }
+
+ const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>(kSourceExpr);
+
+ const auto sourceText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(SourceExpr->getSourceRange()),
+ Result.Context->getSourceManager(), Result.Context->getLangOpts());
+
+ return FixItHint::CreateReplacement(MaterializeExpr->getSourceRange(),
+ ("std::move(" + sourceText + ")").str());
+}
+
+void NonStdMoveChecker::check(const MatchFinder::MatchResult &Result) {
+ // TODO: Include source and target type name in messages.
+
+ const auto *OCE =
+ Result.Nodes.getNodeAs<CXXOperatorCallExpr>(kOperatorCallExpr);
+
+ if (OCE) {
+ const auto *refPtrDecl =
+ dyn_cast<const CXXRecordDecl>(OCE->getCalleeDecl()->getDeclContext());
+
+ const auto XFixItHint = makeFixItHint(Result, OCE);
+ // TODO: produce diagnostic but no FixItHint in this case?
+ if (XFixItHint) {
+ diag(OCE->getBeginLoc(), "non-standard move assignment to %0 obscures "
+ "move, use std::move instead")
+ << refPtrDecl << *XFixItHint;
+ }
+ }
+
+ const auto *CoE = Result.Nodes.getNodeAs<CXXConstructExpr>(kConstructExpr);
+
+ if (CoE) {
+ const auto *refPtrDecl =
+ dyn_cast<const CXXRecordDecl>(CoE->getConstructor()->getDeclContext());
+
+ const auto XFixItHint = makeFixItHint(Result, CoE);
+ // TODO: produce diagnostic but no FixItHint in this case?
+ if (XFixItHint) {
+ diag(CoE->getBeginLoc(), "non-standard move construction of %0 obscures "
+ "move, use std::move instead")
+ << refPtrDecl << *XFixItHint;
+ }
+ }
+
+ // TODO: What about swap calls immediately after default-construction? These
+ // can also be replaced by move-construction, but this may require
+ // control-flow analysis.
+}