llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-tools-extra

Author: wieDasDing (dingxiangfei2009)

<details>
<summary>Changes</summary>

Use of initializer should constitute the use of the base type. It appears that 
we have not been recording such use sites which potentially go indirectly 
through incomplete forward declarations.

---
Full diff: https://github.com/llvm/llvm-project/pull/230070.diff


3 Files Affected:

- (modified) clang-tools-extra/include-cleaner/lib/WalkAST.cpp (+29-4) 
- (modified) clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp (+18) 
- (modified) clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp (+41) 


``````````diff
diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp 
b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
index 5449411371e60..be385a5411762 100644
--- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
+++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
@@ -101,10 +101,14 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
   NamedDecl *getMemberProvider(QualType Base) {
     if (Base->isPointerType())
       return getMemberProvider(Base->getPointeeType());
-    if (const auto *TT = dyn_cast<TypedefType>(Base))
-      return TT->getDecl();
-    if (const auto *UT = dyn_cast<UsingType>(Base))
-      return UT->getDecl();
+    // An alias of an incomplete record does not provide the record definition.
+    if (const auto *RT = Base->getAs<RecordType>();
+        !RT || RT->getDecl()->isCompleteDefinition()) {
+      if (const auto *TT = dyn_cast<TypedefType>(Base))
+        return TT->getDecl();
+      if (const auto *UT = dyn_cast<UsingType>(Base))
+        return UT->getDecl();
+    }
     // A heuristic: to resolve a template type to **only** its template name.
     // We're only using this method for the base type of MemberExpr, in general
     // the template provides the member, and the critical case 
`unique_ptr<Foo>`
@@ -248,6 +252,27 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
     return true;
   }
 
+  bool VisitInitListExpr(InitListExpr *E) {
+    const InitListExpr *Sem = E->isSemanticForm() ? E : E->getSemanticForm();
+    QualType Type = Sem ? Sem->getType() : E->getType();
+    report(E->getBeginLoc(), getMemberProvider(Type), RefType::Implicit);
+    return true;
+  }
+
+  bool VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
+    report(E->getBeginLoc(), getMemberProvider(E->getType()),
+           RefType::Implicit);
+    return true;
+  }
+
+  bool VisitDesignatedInitExpr(DesignatedInitExpr *E) {
+    for (const DesignatedInitExpr::Designator &D : E->designators()) {
+      if (D.isFieldDesignator())
+        report(D.getFieldLoc(), D.getFieldDecl(), RefType::Implicit);
+    }
+    return true;
+  }
+
   bool VisitOverloadExpr(OverloadExpr *E) {
     // Since we can't prove which overloads are used, report all of them.
     for (NamedDecl *D : E->decls())
diff --git a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp 
b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp
index 5df8792c93b2e..22cb9c5abdb24 100644
--- a/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp
@@ -445,6 +445,24 @@ TEST_F(AnalyzeTest, ImplicitOperatorNewDeleteNotUnused) {
   EXPECT_THAT(Results.Unused, testing::IsEmpty());
 }
 
+TEST_F(AnalyzeTest, ForwardDeclaredTypedefAggregateInit) {
+  Inputs.Code = R"cpp(
+#include "fwd.h"
+#include "def.h"
+
+static const Bar b = { 1 };
+)cpp";
+  Inputs.ExtraFiles["fwd.h"] = guard("typedef struct Foo Bar;");
+  Inputs.ExtraFiles["def.h"] = guard("struct Foo { int a; };");
+  TestAST AST(Inputs);
+  auto Decls = AST.context().getTranslationUnitDecl()->decls();
+  auto Results =
+      analyze(std::vector<Decl *>{Decls.begin(), Decls.end()},
+              PP.MacroReferences, PP.Includes, &PI, AST.preprocessor());
+  EXPECT_THAT(Results.Unused, testing::IsEmpty());
+  EXPECT_THAT(Results.Missing, testing::IsEmpty());
+}
+
 TEST(FixIncludes, Basic) {
   llvm::StringRef Code = R"cpp(#include "d.h"
 #include "a.h"
diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp 
b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
index c9519c2e91b68..519368b23ddac 100644
--- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
@@ -472,6 +472,22 @@ TEST(WalkAST, MemberExprs) {
       namespace ns { template<typename> struct Foo { int a; }; }
       using ns::$implicit^Foo;)cpp",
            "void k(Foo<int> b) { b.^a; }");
+  // If the alias points to an incomplete forward declaration, look through to
+  // the underlying record declaration.
+  testWalk(R"cpp(
+      typedef struct $implicit^Foo Bar;
+      struct Foo { int a; };)cpp",
+           "void test(Bar b) { b.^a; }");
+  testWalk(R"cpp(
+      typedef struct $implicit^Foo Bar;
+      typedef Bar Baz;
+      struct Foo { int a; };)cpp",
+           "void test(Baz b) { b.^a; }");
+  testWalk(R"cpp(
+      namespace ns { struct $implicit^Foo; }
+      using Bar = ns::Foo;
+      namespace ns { struct Foo { int a; }; })cpp",
+           "void test(Bar b) { b.^a; }");
   // Test the dependent-type case (CXXDependentScopeMemberExpr)
   testWalk("template<typename T> struct $implicit^Base { void method(); };",
            "template<typename T> void k(Base<T> t) { t.^method(); }");
@@ -487,6 +503,31 @@ TEST(WalkAST, ConstructExprs) {
   testWalk("struct $implicit^S { S(int); };", "S ^t(42);");
   testWalk("struct $implicit^S { S(int); };", "S t = ^42;");
   testWalk("namespace ns { struct S{}; } using ns::$implicit^S;", "S ^t;");
+  testWalk("typedef struct $implicit^S T; struct S {};", "T ^t;");
+}
+
+TEST(WalkAST, InitListExprs) {
+  testWalk("struct $implicit^S { int a; };", "S s = ^{1};");
+  testWalk("struct $implicit^S { int a; };", "S s^(1);");
+  testWalk("struct S { int $implicit^a; };", "S s = {.^a = 1};");
+  testWalk("struct S { int a; }; using $implicit^T = S;", "T s = ^{1};");
+  testWalk(R"cpp(
+      typedef struct $implicit^Foo Bar;
+      struct Foo { int a; };)cpp",
+           "Bar b = ^{1};");
+  testWalk(R"cpp(
+      typedef struct $implicit^Foo Bar;
+      using Baz = Bar;
+      struct Foo { int a; };)cpp",
+           "Baz b = ^{1};");
+  testWalk(R"cpp(
+      typedef struct $implicit^Foo Bar;
+      struct Foo { int a; };)cpp",
+           "Bar b = ^{.a = 1};");
+  testWalk(R"cpp(
+      typedef struct Foo Bar;
+      struct Foo { int $implicit^a; };)cpp",
+           "Bar b = {.^a = 1};");
 }
 
 TEST(WalkAST, Operator) {

``````````

</details>


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

Reply via email to