================
@@ -0,0 +1,354 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseInitStatementCheck.h"
+#include "../utils/ASTUtils.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/QualTypeNames.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include <algorithm> // for std::adjacent_find
+#include <cctype>
+#include <string>
+#include <utility>
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::utils::lexer;
+
+using clang::ast_matchers::internal::Matcher;
+using clang::ast_matchers::internal::VariadicDynCastAllOfMatcher;
+
+namespace clang::tidy::modernize {
+
+namespace {
+
+// Matches CompoundStmt that contains a PrevStmt immediately followed by
+// NextStmt
+// FIXME: use hasAdjSubstatements, see
+// https://github.com/llvm/llvm-project/pull/169965
+AST_MATCHER_P2(CompoundStmt, hasAdjacentStmts, Matcher<Stmt>, DeclMatcher,
+               Matcher<Stmt>, StmtMatcher) {
+  const auto Statements = Node.body();
+
+  return std::adjacent_find(
+             Statements.begin(), Statements.end(),
+             [&](const Stmt *PrevStmt, const Stmt *NextStmt) {
+               clang::ast_matchers::internal::BoundNodesTreeBuilder 
PrevBuilder;
+               if (!DeclMatcher.matches(*PrevStmt, Finder, &PrevBuilder))
+                 return false;
+
+               clang::ast_matchers::internal::BoundNodesTreeBuilder 
NextBuilder;
+               NextBuilder.addMatch(PrevBuilder);
+               if (!StmtMatcher.matches(*NextStmt, Finder, &NextBuilder))
+                 return false;
+
+               Builder->addMatch(NextBuilder);
+               return true;
+             }) != Statements.end();
+}
+
+AST_MATCHER(Decl, isTemplate) { return Node.isTemplated(); }
+
+AST_MATCHER(VarDecl, isUsed) {
+  if (Node.getType().isConstQualified())
+    return true; // FIXME: implement proper "used" check for consts
+  return Node.isUsed();
+}
+
+// got from implementation of memberHasSameNameAsBoundNode mather
+AST_MATCHER_P(VarDecl, hasSameNameAsBoundNode, std::string, BindingID) {
+  auto VarName = Node.getNameAsString();
+
+  return Builder->removeBindings(
+      [this,
+       VarName](const clang::ast_matchers::internal::BoundNodesMap &Nodes) {
+        const DynTypedNode &BN = Nodes.getNode(this->BindingID);
+        if (const auto *ND = BN.get<NamedDecl>()) {
+          if (!isa<BindingDecl, VarDecl>(ND))
+            return true;
+          return ND->getName() != VarName;
+        }
+        return true;
+      });
+}
+
+} // namespace
+
+UseInitStatementCheck::UseInitStatementCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IgnoreConditionVariableStatements(
+          Options.get("IgnoreConditionVariableStatements", false)) {}
+
+void UseInitStatementCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IgnoreConditionVariableStatements",
+                IgnoreConditionVariableStatements);
+}
+
+static Matcher<Stmt> callByRef(const Matcher<Decl> &VarOrBindingNodeMatcher) {
+  const auto ArgMatcher = declRefExpr(to(VarOrBindingNodeMatcher));
+  const auto ParamMatcher = parmVarDecl(hasType(referenceType()));
+
+  return anyOf(
+      callExpr(forEachArgumentWithParam(ArgMatcher, ParamMatcher)),
+      cxxConstructExpr(forEachArgumentWithParam(ArgMatcher, ParamMatcher)));
+}
+
+static Matcher<VarDecl> hasInitializerWithLifetimeExtension() {
+  return hasInitializer(expr(hasDescendant(expr(materializeTemporaryExpr()))));
+}
+
+static Matcher<DeclStmt> includingLifetimeExtension() {
+  return has(varDecl(hasInitializerWithLifetimeExtension()));
+}
+
+static Matcher<DeclStmt> excludingLifetimeExtension() {
+  return unless(includingLifetimeExtension());
+}
+
+// Complete matcher to detect stealing cases, preventing
+// the checker from generating code that could result in dangling references,
+// such as: `int* p; if (int v;cond) { p=&v; } use(p);`
+static Matcher<Stmt> hasStealingMatcher() {
+  const auto IsStealingViaPointer =
+      hasParent(unaryOperator(hasOperatorName("&")));
+  const auto HasLTE = hasInitializerWithLifetimeExtension();
+  const auto HasLTEForBind =
+      hasParent(declStmt(has(varDecl(equalsBoundNode("singleVar"), HasLTE))));
+  const auto IsNonreferenceType = unless(hasType(referenceType()));
+  const auto BoundVar =
+      varDecl(equalsBoundNode("singleVar"), anyOf(IsNonreferenceType, HasLTE));
+  const auto BoundBind = bindingDecl(
+      equalsBoundNode("bindingDecl"),
+      hasParent(decompositionDecl(anyOf(IsNonreferenceType, HasLTEForBind))));
+  const auto VarOrBindingNode = anyOf(BoundVar, BoundBind);
+  const auto StealingAsThis =
+      anyOf(hasParent(memberExpr()),
+            hasParent(implicitCastExpr(hasCastKind(CK_NoOp),
+                                       hasParent(memberExpr()))));
+  const auto HasStealingByPointer =
+      hasDescendant(declRefExpr(to(VarOrBindingNode), IsStealingViaPointer));
+  const auto HasStealingByReference =
+      hasDescendant(callByRef(VarOrBindingNode));
+  const auto HasStealingAsThis =
+      hasDescendant(declRefExpr(to(VarOrBindingNode), StealingAsThis));
+  return anyOf(HasStealingByPointer, HasStealingByReference, 
HasStealingAsThis);
+}
+
+static Matcher<Stmt> hasConflictMatcher() {
+  return hasDescendant(varDecl(anyOf(hasSameNameAsBoundNode("singleVar"),
+                                     hasSameNameAsBoundNode("bindingDecl")))
+                           .bind("conflict"));
+}
+
+static Matcher<Stmt>
+compoundStmtMatcher(const Matcher<Stmt> &StmtMatcher, StringRef StmtName,
+                    const Matcher<Stmt> &PrevStmtMatcher,
+                    const Matcher<Stmt> &RefToBoundMatcher) {
+  const auto NoOtherVarRefs =
+      unless(has(stmt(unless(equalsBoundNode(StmtName.str())),
+                      hasDescendant(RefToBoundMatcher))));
+  return compoundStmt(unless(isExpansionInSystemHeader()),
+                      unless(hasAncestor(functionDecl(isTemplate()))),
+                      hasAdjacentStmts(PrevStmtMatcher, StmtMatcher),
+                      NoOtherVarRefs)
+      .bind("compound");
+}
+
+// Complete matcher for if/switch statements that can be refactored to use
+// init statements.
+// Usage: compoundStmtMatcher(ifStmt, "ifStmt", declStmt(),
+//                            
declRefExpr(to(varDecl(equalsBoundNode("singleVar")))))
+template <typename IfOrSwitchStmt>
+static Matcher<Stmt> compoundStmtMatcher(
+    const VariadicDynCastAllOfMatcher<Stmt, IfOrSwitchStmt> &StmtMatcher,
+    StringRef StmtName, const Matcher<Stmt> &PrevStmtMatcher,
+    const Matcher<Stmt> &RefToBoundMatcher) {
+  const auto StmtWithCondition =
+      StmtMatcher(unless(hasInitStatement(anything())),
+                  unless(hasStealingMatcher()),
+                  hasCondition(expr().bind("condition")),
+                  optionally(hasConflictMatcher()),
+                  optionally(hasConditionVariableStatement(
+                      declStmt().bind("condDeclStmt"))))
+          .bind(StmtName);
+  return compoundStmtMatcher(StmtWithCondition, StmtName, PrevStmtMatcher,
+                             RefToBoundMatcher);
+}
+
+template <bool IsDecomposition = false>
+static auto forBuiltinTypes(const Matcher<ReferenceType> 
&ConditionForReference)
+    -> std::conditional_t<IsDecomposition, Matcher<DecompositionDecl>,
+                          Matcher<Stmt>> {
+  const auto AllowedTypeMatcher = qualType(unless(
+      anyOf(hasCanonicalType(builtinType()), hasCanonicalType(pointerType()),
+            hasCanonicalType(referenceType(ConditionForReference)))));
+
+  if constexpr (IsDecomposition) {
+    const auto NonReferenceVarDecl =
+        varDecl(hasType(qualType(unless(hasCanonicalType(referenceType())))));
+    const auto BindingWithAllowedType =
+        bindingDecl(hasType(AllowedTypeMatcher));
+    const auto DecompositionWithAllowedBinding =
+        unless(has(BindingWithAllowedType));
+    const auto VarDeclInParent =
+        hasParent(declStmt(unless(has(NonReferenceVarDecl))));
+    return anyOf(DecompositionWithAllowedBinding, VarDeclInParent);
+  } else {
+    const auto VarDeclWithAllowedType = varDecl(hasType(AllowedTypeMatcher));
+    return unless(has(VarDeclWithAllowedType));
+  }
+}
+
+// Registers matchers for if/switch statements with regular variable
+// declarations.
+void UseInitStatementCheck::registerVariableDeclMatchers(MatchFinder *Finder) {
+  // Matchers for variable declarations
+  const auto SingleVarDecl = varDecl(isUsed()).bind("singleVar");
+  const auto RefToBoundVarDecl =
+      declRefExpr(to(varDecl(equalsBoundNode("singleVar"))));
+
+  // Matchers for declaration statements that precede if/switch
+  const auto PrevDeclStmt =
+      declStmt(forEach(SingleVarDecl), forBuiltinTypes(anything()),
+               excludingLifetimeExtension())
+          .bind("prevDecl");
+  const auto PrevDeclStmtLTE =
+      declStmt(forEach(SingleVarDecl), forBuiltinTypes(pointee(builtinType())),
+               includingLifetimeExtension())
+          .bind("prevDecl");
+  const auto PrevDeclStmtMatcher = anyOf(PrevDeclStmt, PrevDeclStmtLTE);
+
+  Finder->addMatcher(compoundStmtMatcher(ifStmt, "ifStmt", PrevDeclStmtMatcher,
+                                         RefToBoundVarDecl),
+                     this);
+  Finder->addMatcher(compoundStmtMatcher(switchStmt, "switchStmt",
+                                         PrevDeclStmtMatcher,
+                                         RefToBoundVarDecl),
+                     this);
----------------
denzor200 wrote:

Oke, I will try to use `mapAnyOf` here. This will allow me to get rid of 
`compoundStmtMatcher` templated overload


https://github.com/llvm/llvm-project/pull/171086
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to