Skip to content

Conversation

@AmrDeveloper
Copy link
Member

Previously in case of multiable aliases exists for the same base we just accept the first one

for (const TypedefNameDecl *Alias : Aliases->second) {
BoundNodesTreeBuilder Result(*Builder);
if (Matcher.matches(*Alias, this, &Result)) {
*Builder = std::move(Result);
return true;
}
}
return false;

For example

struct AnInterface {};
typedef AnInterface UnusedTypedef;
typedef AnInterface Base;
class AClass : public Base {};

cxxRecordDecl(isDerivedFrom(decl().bind("typedef"))) typedef will be bonded to UnusedTypedef

But if we changed the order now it will point to the right one

struct AnInterface {};
typedef AnInterface Base;
typedef AnInterface UnusedTypedef;
class AClass : public Base {};

cxxRecordDecl(isDerivedFrom(decl().bind("typedef"))) typedef will be bonded to Base

This PR improve the matcher to prioritise the alias that has same desugared name as the base, if not then just accept the first one.

Fixes: #126498

@AmrDeveloper AmrDeveloper added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Feb 11, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 11, 2025

@llvm/pr-subscribers-clang

Author: Amr Hesham (AmrDeveloper)

Changes

Previously in case of multiable aliases exists for the same base we just accept the first one

for (const TypedefNameDecl *Alias : Aliases->second) {
BoundNodesTreeBuilder Result(*Builder);
if (Matcher.matches(*Alias, this, &Result)) {
*Builder = std::move(Result);
return true;
}
}
return false;

For example

struct AnInterface {};
typedef AnInterface UnusedTypedef;
typedef AnInterface Base;
class AClass : public Base {};

cxxRecordDecl(isDerivedFrom(decl().bind("typedef"))) typedef will be bonded to UnusedTypedef

But if we changed the order now it will point to the right one

struct AnInterface {};
typedef AnInterface Base;
typedef AnInterface UnusedTypedef;
class AClass : public Base {};

cxxRecordDecl(isDerivedFrom(decl().bind("typedef"))) typedef will be bonded to Base

This PR improve the matcher to prioritise the alias that has same desugared name as the base, if not then just accept the first one.

Fixes: #126498


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

2 Files Affected:

  • (modified) clang/lib/ASTMatchers/ASTMatchFinder.cpp (+21)
  • (modified) clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp (+17)
diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
index 3d01a70395a9b..e9ec7eff1e0ab 100644
--- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -1287,6 +1287,27 @@ class MatchASTVisitor : public RecursiveASTVisitor<MatchASTVisitor>,
     auto Aliases = TypeAliases.find(CanonicalType);
     if (Aliases == TypeAliases.end())
       return false;
+
+    if (const auto *ElaboratedTypeNode =
+            llvm::dyn_cast<ElaboratedType>(TypeNode)) {
+      if (ElaboratedTypeNode->isSugared() && Aliases->second.size() > 1) {
+        const auto &DesugaredTypeName =
+            ElaboratedTypeNode->desugar().getAsString();
+
+        for (const TypedefNameDecl *Alias : Aliases->second) {
+          if (Alias->getName() != DesugaredTypeName) {
+            continue;
+          }
+
+          BoundNodesTreeBuilder Result(*Builder);
+          if (Matcher.matches(*Alias, this, &Result)) {
+            *Builder = std::move(Result);
+            return true;
+          }
+        }
+      }
+    }
+
     for (const TypedefNameDecl *Alias : Aliases->second) {
       BoundNodesTreeBuilder Result(*Builder);
       if (Matcher.matches(*Alias, this, &Result)) {
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 5e1c12ba26d87..4e6baedae2be5 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1167,6 +1167,23 @@ TEST_P(ASTMatchersTest, IsDerivedFrom_EmptyName) {
   EXPECT_TRUE(notMatches(Code, cxxRecordDecl(isSameOrDerivedFrom(""))));
 }
 
+TEST_P(ASTMatchersTest, IsDerivedFrom_ElaboratedType) {
+  if (!GetParam().isCXX()) {
+    return;
+  }
+
+  DeclarationMatcher IsDerivenFromBase =
+      cxxRecordDecl(isDerivedFrom(decl().bind("typedef")));
+
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      "struct AnInterface {};"
+      "typedef AnInterface UnusedTypedef;"
+      "typedef AnInterface Base;"
+      "class AClass : public Base {};",
+      IsDerivenFromBase,
+      std::make_unique<VerifyIdIsBoundTo<TypedefDecl>>("typedef", "Base")));
+}
+
 TEST_P(ASTMatchersTest, IsDerivedFrom_ObjC) {
   DeclarationMatcher IsDerivedFromX = objcInterfaceDecl(isDerivedFrom("X"));
   EXPECT_TRUE(

@HighCommander4 HighCommander4 requested review from AaronBallman, alexfh and steveire and removed request for HighCommander4 February 14, 2025 00:15
@HighCommander4
Copy link
Collaborator

HighCommander4 commented Feb 14, 2025

This is a bit more in the weeds of AST matchers internals than what I'm familiar with, so I've added some other reviewers who have made or reviewed more extensive changes to this code based on code history.

@AmrDeveloper
Copy link
Member Author

Please take a look when you have time @AaronBallman @alexfh @steveire

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but you should add a release note to clang/docs/ReleaseNotes.rst so users know about the change in behavior.

@AmrDeveloper AmrDeveloper force-pushed the ast_matcher_drive_from_alias branch from bf17eb2 to 08d0a78 Compare March 5, 2025 17:23
@AmrDeveloper
Copy link
Member Author

LGTM, but you should add a release note to clang/docs/ReleaseNotes.rst so users know about the change in behavior.

Thank you, I added a release note, once you confirm it's approved I can land after CI is green

@AmrDeveloper
Copy link
Member Author

@AaronBallman If releasenote is okey, i can merge now :D

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a minor rewording, feel free to land once you've made the changes.

AST Matchers
------------

- Ensure ``isDerivedFrom`` is matching the correct base in case of more than one aliases exists.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Ensure ``isDerivedFrom`` is matching the correct base in case of more than one aliases exists.
- Ensure ``isDerivedFrom`` matches the correct base in case more than one alias exists.

Minor rewording

@AmrDeveloper AmrDeveloper merged commit c017cdf into llvm:main Mar 5, 2025
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AST Matcher binds wrong TypedefDecl object

4 participants