Skip to content

[Clang][ASTMatcher] Improve matching isDerivedFrom base in case of multi aliases exists #126793

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 5, 2025

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 steveire, alexfh and AaronBallman 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.

@@ -361,6 +361,8 @@ Fixed Point Support in Clang
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
jph-13 pushed a commit to jph-13/llvm-project that referenced this pull request Mar 21, 2025
…lti aliases exists (llvm#126793)

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


https://github.com/llvm/llvm-project/blob/2cf6663d3c86b065edeb693815e6a4b325045cc2/clang/lib/ASTMatchers/ASTMatchFinder.cpp#L1290-L1297

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: llvm#126498
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