[clang] Tracks source location of the selector name within ObjCSelectorExpr (PR #211623)

via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 28 07:03:33 PDT 2026


https://github.com/dmaclach updated https://github.com/llvm/llvm-project/pull/211623

>From cd3085ad07edf3df426f9a855ab49ee1ee457b4b Mon Sep 17 00:00:00 2001
From: Dave MacLachlan <dmaclach at gmail.com>
Date: Thu, 23 Jul 2026 10:12:21 -0700
Subject: [PATCH 1/2] Adds tracking for the source location of the selector
 name within ObjCSelectorExpr.

Simplifies some work I am doing in include-cleaner and could be used to improve
diagnostics for selector locations that are currently pointing at `@selector` instead of
the actual selector name.
---
 clang/include/clang/AST/ExprObjC.h        |  8 +++++---
 clang/include/clang/Sema/SemaObjC.h       |  3 ++-
 clang/lib/Parse/ParseObjc.cpp             |  8 ++++----
 clang/lib/Sema/SemaExprObjC.cpp           | 21 ++++++++++-----------
 clang/lib/Serialization/ASTReaderStmt.cpp |  1 +
 clang/lib/Serialization/ASTWriterStmt.cpp |  1 +
 6 files changed, 23 insertions(+), 19 deletions(-)

diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h
index 00c9b44930bcc..571dfadea3e90 100644
--- a/clang/include/clang/AST/ExprObjC.h
+++ b/clang/include/clang/AST/ExprObjC.h
@@ -485,13 +485,13 @@ class ObjCEncodeExpr : public Expr {
 /// ObjCSelectorExpr used for \@selector in Objective-C.
 class ObjCSelectorExpr : public Expr {
   Selector SelName;
-  SourceLocation AtLoc, RParenLoc;
+  SourceLocation AtLoc, SelLoc, RParenLoc;
 
 public:
   ObjCSelectorExpr(QualType T, Selector selInfo, SourceLocation at,
-                   SourceLocation rp)
+                   SourceLocation sel, SourceLocation rp)
       : Expr(ObjCSelectorExprClass, T, VK_PRValue, OK_Ordinary),
-        SelName(selInfo), AtLoc(at), RParenLoc(rp) {
+        SelName(selInfo), AtLoc(at), SelLoc(sel), RParenLoc(rp) {
     setDependence(ExprDependence::None);
   }
   explicit ObjCSelectorExpr(EmptyShell Empty)
@@ -501,8 +501,10 @@ class ObjCSelectorExpr : public Expr {
   void setSelector(Selector S) { SelName = S; }
 
   SourceLocation getAtLoc() const { return AtLoc; }
+  SourceLocation getSelectorLoc() const { return SelLoc; }
   SourceLocation getRParenLoc() const { return RParenLoc; }
   void setAtLoc(SourceLocation L) { AtLoc = L; }
+  void setSelectorLoc(SourceLocation L) { SelLoc = L; }
   void setRParenLoc(SourceLocation L) { RParenLoc = L; }
 
   SourceLocation getBeginLoc() const LLVM_READONLY { return AtLoc; }
diff --git a/clang/include/clang/Sema/SemaObjC.h b/clang/include/clang/Sema/SemaObjC.h
index f5cb8ea4b034a..89d450fb3ddd0 100644
--- a/clang/include/clang/Sema/SemaObjC.h
+++ b/clang/include/clang/Sema/SemaObjC.h
@@ -691,7 +691,8 @@ class SemaObjC : public SemaBase {
 
   /// ParseObjCSelectorExpression - Build selector expression for \@selector
   ExprResult ParseObjCSelectorExpression(Selector Sel, SourceLocation AtLoc,
-                                         SourceLocation SelLoc,
+                                         SourceLocation SelectorLoc,
+                                         SourceLocation SelNameLoc,
                                          SourceLocation LParenLoc,
                                          SourceLocation RParenLoc,
                                          bool WarnMultipleSelectors);
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp
index eb50b0e8546c6..a6fe60b43c984 100644
--- a/clang/lib/Parse/ParseObjc.cpp
+++ b/clang/lib/Parse/ParseObjc.cpp
@@ -3205,7 +3205,7 @@ ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) {
     return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@selector");
 
   SmallVector<const IdentifierInfo *, 12> KeyIdents;
-  SourceLocation sLoc;
+  SourceLocation SelectorNameLoc;
 
   BalancedDelimiterTracker T(*this, tok::l_paren);
   T.consumeOpen();
@@ -3219,7 +3219,7 @@ ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) {
     return ExprError();
   }
 
-  IdentifierInfo *SelIdent = ParseObjCSelectorPiece(sLoc);
+  IdentifierInfo *SelIdent = ParseObjCSelectorPiece(SelectorNameLoc);
   if (!SelIdent &&  // missing selector name.
       Tok.isNot(tok::colon) && Tok.isNot(tok::coloncolon))
     return ExprError(Diag(Tok, diag::err_expected) << tok::identifier);
@@ -3259,8 +3259,8 @@ ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) {
   T.consumeClose();
   Selector Sel = PP.getSelectorTable().getSelector(nColons, &KeyIdents[0]);
   return Actions.ObjC().ParseObjCSelectorExpression(
-      Sel, AtLoc, SelectorLoc, T.getOpenLocation(), T.getCloseLocation(),
-      !HasOptionalParen);
+      Sel, AtLoc, SelectorLoc, SelectorNameLoc, T.getOpenLocation(),
+      T.getCloseLocation(), !HasOptionalParen);
 }
 
 void Parser::ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod) {
diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp
index c166e0c9e7b72..935507f9c5589 100644
--- a/clang/lib/Sema/SemaExprObjC.cpp
+++ b/clang/lib/Sema/SemaExprObjC.cpp
@@ -1416,12 +1416,10 @@ static ObjCMethodDecl *findMethodInCurrentClass(Sema &S, Selector Sel) {
   return nullptr;
 }
 
-ExprResult SemaObjC::ParseObjCSelectorExpression(Selector Sel,
-                                                 SourceLocation AtLoc,
-                                                 SourceLocation SelLoc,
-                                                 SourceLocation LParenLoc,
-                                                 SourceLocation RParenLoc,
-                                                 bool WarnMultipleSelectors) {
+ExprResult SemaObjC::ParseObjCSelectorExpression(
+    Selector Sel, SourceLocation AtLoc, SourceLocation SelectorLoc,
+    SourceLocation SelNameLoc, SourceLocation LParenLoc,
+    SourceLocation RParenLoc, bool WarnMultipleSelectors) {
   ASTContext &Context = getASTContext();
   ObjCMethodDecl *Method = LookupInstanceMethodInGlobalPool(Sel,
                              SourceRange(LParenLoc, RParenLoc));
@@ -1433,12 +1431,13 @@ ExprResult SemaObjC::ParseObjCSelectorExpression(Selector Sel,
       Selector MatchedSel = OM->getSelector();
       SourceRange SelectorRange(LParenLoc.getLocWithOffset(1),
                                 RParenLoc.getLocWithOffset(-1));
-      Diag(SelLoc, diag::warn_undeclared_selector_with_typo)
-        << Sel << MatchedSel
-        << FixItHint::CreateReplacement(SelectorRange, MatchedSel.getAsString());
+      Diag(SelectorLoc, diag::warn_undeclared_selector_with_typo)
+          << Sel << MatchedSel
+          << FixItHint::CreateReplacement(SelectorRange,
+                                          MatchedSel.getAsString());
 
     } else
-        Diag(SelLoc, diag::warn_undeclared_selector) << Sel;
+      Diag(SelectorLoc, diag::warn_undeclared_selector) << Sel;
   } else {
     DiagnoseMismatchedSelectors(SemaRef, AtLoc, Method, LParenLoc, RParenLoc,
                                 WarnMultipleSelectors);
@@ -1509,7 +1508,7 @@ ExprResult SemaObjC::ParseObjCSelectorExpression(Selector Sel,
     }
   }
   QualType Ty = Context.getObjCSelType();
-  return new (Context) ObjCSelectorExpr(Ty, Sel, AtLoc, RParenLoc);
+  return new (Context) ObjCSelectorExpr(Ty, Sel, AtLoc, SelNameLoc, RParenLoc);
 }
 
 ExprResult SemaObjC::ParseObjCProtocolExpression(IdentifierInfo *ProtocolId,
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 87cec16a76323..21ca0ec339df0 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1574,6 +1574,7 @@ void ASTStmtReader::VisitObjCSelectorExpr(ObjCSelectorExpr *E) {
   VisitExpr(E);
   E->setSelector(Record.readSelector());
   E->setAtLoc(readSourceLocation());
+  E->setSelectorLoc(readSourceLocation());
   E->setRParenLoc(readSourceLocation());
 }
 
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 70477f4cf4001..97a17277759d0 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1536,6 +1536,7 @@ void ASTStmtWriter::VisitObjCSelectorExpr(ObjCSelectorExpr *E) {
   VisitExpr(E);
   Record.AddSelectorRef(E->getSelector());
   Record.AddSourceLocation(E->getAtLoc());
+  Record.AddSourceLocation(E->getSelectorLoc());
   Record.AddSourceLocation(E->getRParenLoc());
   Code = serialization::EXPR_OBJC_SELECTOR_EXPR;
 }

>From f6b73505e40dd3822a9a10432fc2e212c0084fe6 Mon Sep 17 00:00:00 2001
From: Dave MacLachlan <dmaclach at gmail.com>
Date: Tue, 28 Jul 2026 06:59:05 -0700
Subject: [PATCH 2/2] Cleaned up some naming and added tests.

---
 clang/include/clang/AST/ExprObjC.h         |  10 +-
 clang/include/clang/Sema/SemaObjC.h        |   2 +-
 clang/lib/Parse/ParseObjc.cpp              |   4 +-
 clang/lib/Sema/SemaExprObjC.cpp            |  11 +-
 clang/lib/Serialization/ASTReaderStmt.cpp  |   2 +-
 clang/lib/Serialization/ASTWriterStmt.cpp  |   2 +-
 clang/unittests/AST/SourceLocationTest.cpp | 249 +++++++++++++++------
 7 files changed, 194 insertions(+), 86 deletions(-)

diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h
index 571dfadea3e90..7f7b556f14329 100644
--- a/clang/include/clang/AST/ExprObjC.h
+++ b/clang/include/clang/AST/ExprObjC.h
@@ -485,13 +485,13 @@ class ObjCEncodeExpr : public Expr {
 /// ObjCSelectorExpr used for \@selector in Objective-C.
 class ObjCSelectorExpr : public Expr {
   Selector SelName;
-  SourceLocation AtLoc, SelLoc, RParenLoc;
+  SourceLocation AtLoc, SelNameLoc, RParenLoc;
 
 public:
   ObjCSelectorExpr(QualType T, Selector selInfo, SourceLocation at,
-                   SourceLocation sel, SourceLocation rp)
+                   SourceLocation selNameLoc, SourceLocation rp)
       : Expr(ObjCSelectorExprClass, T, VK_PRValue, OK_Ordinary),
-        SelName(selInfo), AtLoc(at), SelLoc(sel), RParenLoc(rp) {
+        SelName(selInfo), AtLoc(at), SelNameLoc(selNameLoc), RParenLoc(rp) {
     setDependence(ExprDependence::None);
   }
   explicit ObjCSelectorExpr(EmptyShell Empty)
@@ -501,10 +501,10 @@ class ObjCSelectorExpr : public Expr {
   void setSelector(Selector S) { SelName = S; }
 
   SourceLocation getAtLoc() const { return AtLoc; }
-  SourceLocation getSelectorLoc() const { return SelLoc; }
+  SourceLocation getSelectorNameLoc() const { return SelNameLoc; }
   SourceLocation getRParenLoc() const { return RParenLoc; }
   void setAtLoc(SourceLocation L) { AtLoc = L; }
-  void setSelectorLoc(SourceLocation L) { SelLoc = L; }
+  void setSelectorNameLoc(SourceLocation L) { SelNameLoc = L; }
   void setRParenLoc(SourceLocation L) { RParenLoc = L; }
 
   SourceLocation getBeginLoc() const LLVM_READONLY { return AtLoc; }
diff --git a/clang/include/clang/Sema/SemaObjC.h b/clang/include/clang/Sema/SemaObjC.h
index 89d450fb3ddd0..e597e5845f035 100644
--- a/clang/include/clang/Sema/SemaObjC.h
+++ b/clang/include/clang/Sema/SemaObjC.h
@@ -691,7 +691,7 @@ class SemaObjC : public SemaBase {
 
   /// ParseObjCSelectorExpression - Build selector expression for \@selector
   ExprResult ParseObjCSelectorExpression(Selector Sel, SourceLocation AtLoc,
-                                         SourceLocation SelectorLoc,
+                                         SourceLocation SelKWLoc,
                                          SourceLocation SelNameLoc,
                                          SourceLocation LParenLoc,
                                          SourceLocation RParenLoc,
diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp
index a6fe60b43c984..d01b0abf917cb 100644
--- a/clang/lib/Parse/ParseObjc.cpp
+++ b/clang/lib/Parse/ParseObjc.cpp
@@ -3199,7 +3199,7 @@ Parser::ParseObjCProtocolExpression(SourceLocation AtLoc) {
 }
 
 ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) {
-  SourceLocation SelectorLoc = ConsumeToken();
+  SourceLocation SelectorKeywordLoc = ConsumeToken();
 
   if (Tok.isNot(tok::l_paren))
     return ExprError(Diag(Tok, diag::err_expected_lparen_after) << "@selector");
@@ -3259,7 +3259,7 @@ ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) {
   T.consumeClose();
   Selector Sel = PP.getSelectorTable().getSelector(nColons, &KeyIdents[0]);
   return Actions.ObjC().ParseObjCSelectorExpression(
-      Sel, AtLoc, SelectorLoc, SelectorNameLoc, T.getOpenLocation(),
+      Sel, AtLoc, SelectorKeywordLoc, SelectorNameLoc, T.getOpenLocation(),
       T.getCloseLocation(), !HasOptionalParen);
 }
 
diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp
index 935507f9c5589..a8f080c02ccec 100644
--- a/clang/lib/Sema/SemaExprObjC.cpp
+++ b/clang/lib/Sema/SemaExprObjC.cpp
@@ -1417,7 +1417,7 @@ static ObjCMethodDecl *findMethodInCurrentClass(Sema &S, Selector Sel) {
 }
 
 ExprResult SemaObjC::ParseObjCSelectorExpression(
-    Selector Sel, SourceLocation AtLoc, SourceLocation SelectorLoc,
+    Selector Sel, SourceLocation AtLoc, SourceLocation SelKWLoc,
     SourceLocation SelNameLoc, SourceLocation LParenLoc,
     SourceLocation RParenLoc, bool WarnMultipleSelectors) {
   ASTContext &Context = getASTContext();
@@ -1431,13 +1431,12 @@ ExprResult SemaObjC::ParseObjCSelectorExpression(
       Selector MatchedSel = OM->getSelector();
       SourceRange SelectorRange(LParenLoc.getLocWithOffset(1),
                                 RParenLoc.getLocWithOffset(-1));
-      Diag(SelectorLoc, diag::warn_undeclared_selector_with_typo)
-          << Sel << MatchedSel
-          << FixItHint::CreateReplacement(SelectorRange,
-                                          MatchedSel.getAsString());
+      Diag(SelKWLoc, diag::warn_undeclared_selector_with_typo)
+        << Sel << MatchedSel
+        << FixItHint::CreateReplacement(SelectorRange, MatchedSel.getAsString());
 
     } else
-      Diag(SelectorLoc, diag::warn_undeclared_selector) << Sel;
+        Diag(SelKWLoc, diag::warn_undeclared_selector) << Sel;
   } else {
     DiagnoseMismatchedSelectors(SemaRef, AtLoc, Method, LParenLoc, RParenLoc,
                                 WarnMultipleSelectors);
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 21ca0ec339df0..ae8de63c1bd6b 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1574,7 +1574,7 @@ void ASTStmtReader::VisitObjCSelectorExpr(ObjCSelectorExpr *E) {
   VisitExpr(E);
   E->setSelector(Record.readSelector());
   E->setAtLoc(readSourceLocation());
-  E->setSelectorLoc(readSourceLocation());
+  E->setSelectorNameLoc(readSourceLocation());
   E->setRParenLoc(readSourceLocation());
 }
 
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 97a17277759d0..10443d42df8c0 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1536,7 +1536,7 @@ void ASTStmtWriter::VisitObjCSelectorExpr(ObjCSelectorExpr *E) {
   VisitExpr(E);
   Record.AddSelectorRef(E->getSelector());
   Record.AddSourceLocation(E->getAtLoc());
-  Record.AddSourceLocation(E->getSelectorLoc());
+  Record.AddSourceLocation(E->getSelectorNameLoc());
   Record.AddSourceLocation(E->getRParenLoc());
   Code = serialization::EXPR_OBJC_SELECTOR_EXPR;
 }
diff --git a/clang/unittests/AST/SourceLocationTest.cpp b/clang/unittests/AST/SourceLocationTest.cpp
index 7bd0de04b2582..507032053e0ae 100644
--- a/clang/unittests/AST/SourceLocationTest.cpp
+++ b/clang/unittests/AST/SourceLocationTest.cpp
@@ -19,13 +19,19 @@
 #include "clang/AST/ASTConcept.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTFwd.h"
+#include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/ExprConcepts.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Testing/CommandLineArgs.h"
 #include "clang/Tooling/Tooling.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/Testing/Annotations/Annotations.h"
 #include "gtest/gtest.h"
+#include <string>
 
 using namespace clang;
 using namespace clang::ast_matchers;
@@ -177,7 +183,7 @@ TEST(MemberExpr, ImplicitMemberRange) {
 class MemberExprArrowLocVerifier : public RangeVerifier<MemberExpr> {
 protected:
   SourceRange getRange(const MemberExpr &Node) override {
-     return Node.getOperatorLoc();
+    return Node.getOperatorLoc();
   }
 };
 
@@ -209,8 +215,7 @@ TEST(MemberExpr, ImplicitArrowRange) {
 TEST(VarDecl, VMTypeFixedVarDeclRange) {
   RangeVerifier<VarDecl> Verifier;
   Verifier.expectRange(1, 1, 1, 23);
-  EXPECT_TRUE(Verifier.match("int a[(int)(void*)1234];",
-                             varDecl(), Lang_C89));
+  EXPECT_TRUE(Verifier.match("int a[(int)(void*)1234];", varDecl(), Lang_C89));
 }
 
 TEST(TypeLoc, IntRange) {
@@ -366,9 +371,10 @@ TEST(CXXConstructorDecl, DeletedCtorLocRange) {
 TEST(CompoundLiteralExpr, CompoundVectorLiteralRange) {
   RangeVerifier<CompoundLiteralExpr> Verifier;
   Verifier.expectRange(2, 11, 2, 22);
-  EXPECT_TRUE(Verifier.match(
-                  "typedef int int2 __attribute__((ext_vector_type(2)));\n"
-                  "int2 i2 = (int2){1, 2};", compoundLiteralExpr()));
+  EXPECT_TRUE(
+      Verifier.match("typedef int int2 __attribute__((ext_vector_type(2)));\n"
+                     "int2 i2 = (int2){1, 2};",
+                     compoundLiteralExpr()));
 }
 
 TEST(CompoundLiteralExpr, ParensCompoundVectorLiteralRange) {
@@ -383,17 +389,19 @@ TEST(CompoundLiteralExpr, ParensCompoundVectorLiteralRange) {
 TEST(InitListExpr, VectorLiteralListBraceRange) {
   RangeVerifier<InitListExpr> Verifier;
   Verifier.expectRange(2, 17, 2, 22);
-  EXPECT_TRUE(Verifier.match(
-                  "typedef int int2 __attribute__((ext_vector_type(2)));\n"
-                  "int2 i2 = (int2){1, 2};", initListExpr()));
+  EXPECT_TRUE(
+      Verifier.match("typedef int int2 __attribute__((ext_vector_type(2)));\n"
+                     "int2 i2 = (int2){1, 2};",
+                     initListExpr()));
 }
 
 TEST(InitListExpr, VectorLiteralInitListParens) {
   RangeVerifier<InitListExpr> Verifier;
   Verifier.expectRange(2, 26, 2, 31);
-  EXPECT_TRUE(Verifier.match(
-                  "typedef int int2 __attribute__((ext_vector_type(2)));\n"
-                  "constant int2 i2 = (int2)(1, 2);", initListExpr(), Lang_OpenCL));
+  EXPECT_TRUE(
+      Verifier.match("typedef int int2 __attribute__((ext_vector_type(2)));\n"
+                     "constant int2 i2 = (int2)(1, 2);",
+                     initListExpr(), Lang_OpenCL));
 }
 
 class TemplateAngleBracketLocRangeVerifier : public RangeVerifier<TypeLoc> {
@@ -409,10 +417,10 @@ class TemplateAngleBracketLocRangeVerifier : public RangeVerifier<TypeLoc> {
 TEST(TemplateSpecializationTypeLoc, AngleBracketLocations) {
   TemplateAngleBracketLocRangeVerifier Verifier;
   Verifier.expectRange(2, 8, 2, 10);
-  EXPECT_TRUE(Verifier.match(
-      "template<typename T> struct A {}; struct B{}; void f(\n"
-      "const A<B>&);",
-      loc(templateSpecializationType())));
+  EXPECT_TRUE(
+      Verifier.match("template<typename T> struct A {}; struct B{}; void f(\n"
+                     "const A<B>&);",
+                     loc(templateSpecializationType())));
 }
 
 TEST(CXXNewExpr, TypeParenRange) {
@@ -434,12 +442,11 @@ class UnaryTransformTypeLocParensRangeVerifier : public RangeVerifier<TypeLoc> {
 TEST(UnaryTransformTypeLoc, ParensRange) {
   UnaryTransformTypeLocParensRangeVerifier Verifier;
   Verifier.expectRange(3, 26, 3, 28);
-  EXPECT_TRUE(Verifier.match(
-      "template <typename T>\n"
-      "struct S {\n"
-      "typedef __underlying_type(T) type;\n"
-      "};",
-      loc(unaryTransformType())));
+  EXPECT_TRUE(Verifier.match("template <typename T>\n"
+                             "struct S {\n"
+                             "typedef __underlying_type(T) type;\n"
+                             "};",
+                             loc(unaryTransformType())));
 }
 
 TEST(PointerTypeLoc, StarLoc) {
@@ -451,11 +458,11 @@ TEST(PointerTypeLoc, StarLoc) {
   SourceManager &SM = AST->getSourceManager();
   auto &Ctx = AST->getASTContext();
 
-  auto *VD = selectFirst<VarDecl>("vd", match(varDecl(hasName("var")).bind("vd"), Ctx));
+  auto *VD = selectFirst<VarDecl>(
+      "vd", match(varDecl(hasName("var")).bind("vd"), Ctx));
   ASSERT_NE(VD, nullptr);
 
-  auto TL =
-      VD->getTypeSourceInfo()->getTypeLoc().castAs<PointerTypeLoc>();
+  auto TL = VD->getTypeSourceInfo()->getTypeLoc().castAs<PointerTypeLoc>();
   ASSERT_EQ(SM.getFileOffset(TL.getStarLoc()), Example.point("star"));
 }
 
@@ -469,47 +476,49 @@ TEST(PointerTypeLoc, StarLocBehindSugar) {
   SourceManager &SM = AST->getSourceManager();
   auto &Ctx = AST->getASTContext();
 
-  auto *VD = selectFirst<VarDecl>("vd", match(varDecl(hasName("var")).bind("vd"), Ctx));
+  auto *VD = selectFirst<VarDecl>(
+      "vd", match(varDecl(hasName("var")).bind("vd"), Ctx));
   ASSERT_NE(VD, nullptr);
 
   auto TL = VD->getTypeSourceInfo()->getTypeLoc().castAs<PointerTypeLoc>();
   EXPECT_EQ(SM.getFileOffset(TL.getStarLoc()), Example.point("2nd"));
 
   // Cast intermediate TypeLoc to make sure the structure matches expectations.
-  auto InnerPtrTL = TL.getPointeeLoc().castAs<AttributedTypeLoc>()
-    .getNextTypeLoc().castAs<MacroQualifiedTypeLoc>()
-    .getNextTypeLoc().castAs<AttributedTypeLoc>()
-    .getNextTypeLoc().castAs<PointerTypeLoc>();
+  auto InnerPtrTL = TL.getPointeeLoc()
+                        .castAs<AttributedTypeLoc>()
+                        .getNextTypeLoc()
+                        .castAs<MacroQualifiedTypeLoc>()
+                        .getNextTypeLoc()
+                        .castAs<AttributedTypeLoc>()
+                        .getNextTypeLoc()
+                        .castAs<PointerTypeLoc>();
   EXPECT_EQ(SM.getFileOffset(InnerPtrTL.getStarLoc()), Example.point("1st"));
 }
 
 TEST(CXXFunctionalCastExpr, SourceRange) {
   RangeVerifier<CXXFunctionalCastExpr> Verifier;
   Verifier.expectRange(2, 10, 2, 14);
-  EXPECT_TRUE(Verifier.match(
-      "int foo() {\n"
-      "  return int{};\n"
-      "}",
-      cxxFunctionalCastExpr(), Lang_CXX11));
+  EXPECT_TRUE(Verifier.match("int foo() {\n"
+                             "  return int{};\n"
+                             "}",
+                             cxxFunctionalCastExpr(), Lang_CXX11));
 }
 
 TEST(CXXConstructExpr, SourceRange) {
   RangeVerifier<CXXConstructExpr> Verifier;
   Verifier.expectRange(3, 14, 3, 19);
-  EXPECT_TRUE(Verifier.match(
-      "struct A { A(int, int); };\n"
-      "void f(A a);\n"
-      "void g() { f({0, 0}); }",
-      cxxConstructExpr(), Lang_CXX11));
+  EXPECT_TRUE(Verifier.match("struct A { A(int, int); };\n"
+                             "void f(A a);\n"
+                             "void g() { f({0, 0}); }",
+                             cxxConstructExpr(), Lang_CXX11));
 }
 
 TEST(CXXTemporaryObjectExpr, SourceRange) {
   RangeVerifier<CXXTemporaryObjectExpr> Verifier;
   Verifier.expectRange(2, 6, 2, 12);
-  EXPECT_TRUE(Verifier.match(
-      "struct A { A(int, int); };\n"
-      "A a( A{0, 0} );",
-      cxxTemporaryObjectExpr(), Lang_CXX11));
+  EXPECT_TRUE(Verifier.match("struct A { A(int, int); };\n"
+                             "A a( A{0, 0} );",
+                             cxxTemporaryObjectExpr(), Lang_CXX11));
 }
 
 TEST(CXXUnresolvedConstructExpr, SourceRange) {
@@ -517,32 +526,29 @@ TEST(CXXUnresolvedConstructExpr, SourceRange) {
   Verifier.expectRange(3, 10, 3, 12);
   std::vector<std::string> Args;
   Args.push_back("-fno-delayed-template-parsing");
-  EXPECT_TRUE(Verifier.match(
-      "template <typename U>\n"
-      "U foo() {\n"
-      "  return U{};\n"
-      "}",
-      cxxUnresolvedConstructExpr(), Args, Lang_CXX11));
+  EXPECT_TRUE(Verifier.match("template <typename U>\n"
+                             "U foo() {\n"
+                             "  return U{};\n"
+                             "}",
+                             cxxUnresolvedConstructExpr(), Args, Lang_CXX11));
 }
 
 TEST(UsingDecl, SourceRange) {
   RangeVerifier<UsingDecl> Verifier;
   Verifier.expectRange(2, 22, 2, 25);
-  EXPECT_TRUE(Verifier.match(
-      "class B { protected: int i; };\n"
-      "class D : public B { B::i; };",
-      usingDecl()));
+  EXPECT_TRUE(Verifier.match("class B { protected: int i; };\n"
+                             "class D : public B { B::i; };",
+                             usingDecl()));
 }
 
 TEST(UnresolvedUsingValueDecl, SourceRange) {
   RangeVerifier<UnresolvedUsingValueDecl> Verifier;
   Verifier.expectRange(3, 3, 3, 6);
-  EXPECT_TRUE(Verifier.match(
-      "template <typename B>\n"
-      "class D : public B {\n"
-      "  B::i;\n"
-      "};",
-      unresolvedUsingValueDecl()));
+  EXPECT_TRUE(Verifier.match("template <typename B>\n"
+                             "class D : public B {\n"
+                             "  B::i;\n"
+                             "};",
+                             unresolvedUsingValueDecl()));
 }
 
 TEST(FriendDecl, FriendNonMemberFunctionLocation) {
@@ -782,9 +788,7 @@ TEST(ObjCMessageExpr, ParenExprRange) {
 TEST(FunctionDecl, FunctionDeclWithThrowSpecification) {
   RangeVerifier<FunctionDecl> Verifier;
   Verifier.expectRange(1, 1, 1, 16);
-  EXPECT_TRUE(Verifier.match(
-      "void f() throw();\n",
-      functionDecl()));
+  EXPECT_TRUE(Verifier.match("void f() throw();\n", functionDecl()));
 }
 
 TEST(FunctionDecl, FunctionDeclWithNoExceptSpecification) {
@@ -903,11 +907,10 @@ TEST(FunctionDeclParameters, FunctionDeclWithParamAttribute) {
 TEST(CXXMethodDecl, CXXMethodDeclWithThrowSpecification) {
   RangeVerifier<FunctionDecl> Verifier;
   Verifier.expectRange(2, 1, 2, 16);
-  EXPECT_TRUE(Verifier.match(
-      "class A {\n"
-      "void f() throw();\n"
-      "};\n",
-      functionDecl()));
+  EXPECT_TRUE(Verifier.match("class A {\n"
+                             "void f() throw();\n"
+                             "};\n",
+                             functionDecl()));
 }
 
 TEST(CXXMethodDecl, CXXMethodDeclWithNoExceptSpecification) {
@@ -922,8 +925,7 @@ TEST(CXXMethodDecl, CXXMethodDeclWithNoExceptSpecification) {
 class ExceptionSpecRangeVerifier : public RangeVerifier<TypeLoc> {
 protected:
   SourceRange getRange(const TypeLoc &Node) override {
-    auto T =
-      Node.getUnqualifiedLoc().castAs<FunctionProtoTypeLoc>();
+    auto T = Node.getUnqualifiedLoc().castAs<FunctionProtoTypeLoc>();
     assert(!T.isNull());
     return T.getExceptionSpecRange();
   }
@@ -1129,4 +1131,111 @@ template <int X> requires NS::CCC<X> int z = X;
   EXPECT_TRUE(Verifier.match(Code2, varTemplateDecl(hasName("z")), Lang_CXX20));
 }
 
+class ObjCSelectorLocationVerifier : public MatchVerifier<ObjCSelectorExpr> {
+  unsigned ExpectAtLine = 0, ExpectAtColumn = 0;
+  unsigned ExpectNameLine = 0, ExpectNameColumn = 0;
+  unsigned ExpectRParenLine = 0, ExpectRParenColumn = 0;
+
+public:
+  void expectLocations(unsigned AtLine, unsigned AtColumn, unsigned NameLine,
+                       unsigned NameColumn, unsigned RParenLine,
+                       unsigned RParenColumn) {
+    ExpectAtLine = AtLine;
+    ExpectAtColumn = AtColumn;
+    ExpectNameLine = NameLine;
+    ExpectNameColumn = NameColumn;
+    ExpectRParenLine = RParenLine;
+    ExpectRParenColumn = RParenColumn;
+  }
+
+protected:
+  void verify(const MatchFinder::MatchResult &Result,
+              const ObjCSelectorExpr &Node) override {
+    SourceLocation AtLoc = Node.getAtLoc();
+    SourceLocation NameLoc = Node.getSelectorNameLoc();
+    SourceLocation RParenLoc = Node.getRParenLoc();
+
+    unsigned AtLine = Result.SourceManager->getSpellingLineNumber(AtLoc);
+    unsigned AtColumn = Result.SourceManager->getSpellingColumnNumber(AtLoc);
+    unsigned NameLine = Result.SourceManager->getSpellingLineNumber(NameLoc);
+    unsigned NameColumn =
+        Result.SourceManager->getSpellingColumnNumber(NameLoc);
+    unsigned RParenLine =
+        Result.SourceManager->getSpellingLineNumber(RParenLoc);
+    unsigned RParenColumn =
+        Result.SourceManager->getSpellingColumnNumber(RParenLoc);
+
+    if (AtLine != ExpectAtLine || AtColumn != ExpectAtColumn ||
+        NameLine != ExpectNameLine || NameColumn != ExpectNameColumn ||
+        RParenLine != ExpectRParenLine || RParenColumn != ExpectRParenColumn) {
+      std::string MsgStr;
+      llvm::raw_string_ostream Msg(MsgStr);
+      Msg << "Expected At Location <" << ExpectAtLine << ":" << ExpectAtColumn
+          << ">, found <";
+      AtLoc.print(Msg, *Result.SourceManager);
+      Msg << ">\n";
+
+      Msg << "Expected Name Location <" << ExpectNameLine << ":"
+          << ExpectNameColumn << ">, found <";
+      NameLoc.print(Msg, *Result.SourceManager);
+      Msg << ">\n";
+
+      Msg << "Expected RParen Location <" << ExpectRParenLine << ":"
+          << ExpectRParenColumn << ">, found <";
+      RParenLoc.print(Msg, *Result.SourceManager);
+      Msg << ">";
+
+      this->setFailure(MsgStr);
+    }
+  }
+};
+
+const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCSelectorExpr>
+    objcSelectorExpr;
+
+TEST(ObjCSelectorExpr, LocationsValidation) {
+  ObjCSelectorLocationVerifier Verifier;
+
+  Verifier.expectLocations(
+      /*At:*/ 2, 11,
+      /*Name:*/ 2, 21,
+      /*RParen:*/ 2, 28);
+
+  EXPECT_TRUE(Verifier.match("void foo() {\n"
+                             "  SEL s = @selector(foobar:);\n"
+                             "}\n",
+                             traverse(TK_AsIs, objcSelectorExpr()), Lang_OBJC));
+}
+
+TEST(ObjCSelectorExpr, LocationsValidationSpaces) {
+  ObjCSelectorLocationVerifier Verifier;
+
+  Verifier.expectLocations(
+      /*At:*/ 2, 13,
+      /*Name:*/ 2, 27,
+      /*RParen:*/ 2, 36);
+
+  EXPECT_TRUE(Verifier.match("void foo() {\n"
+                             "  SEL s =   @  selector(  foobar   );\n"
+                             "}\n",
+                             traverse(TK_AsIs, objcSelectorExpr()), Lang_OBJC));
+}
+
+TEST(ObjCSelectorExpr, LocationsValidationBrokenLines) {
+  ObjCSelectorLocationVerifier Verifier;
+
+  Verifier.expectLocations(
+      /*At:*/ 3, 5,
+      /*Name:*/ 4, 7,
+      /*RParen:*/ 5, 5);
+
+  EXPECT_TRUE(Verifier.match("void foo() {\n"
+                             "  SEL s =\n"
+                             "    @selector(\n"
+                             "      foobar\n"
+                             "    );\n"
+                             "}\n",
+                             traverse(TK_AsIs, objcSelectorExpr()), Lang_OBJC));
+}
+
 } // end namespace



More information about the cfe-commits mailing list