summarylogtreecommitdiffstats
path: root/hover-virt-offset.patch
blob: 5a322c7db8108e8dd215e0029101b5d8b7aa953a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp
index ef3523211..4cbbcfcd1 100644
--- a/clang-tools-extra/clangd/Hover.cpp
+++ b/clang-tools-extra/clangd/Hover.cpp
@@ -993,6 +993,131 @@ bool isHardLineBreakAfter(llvm::StringRef Line, llvm::StringRef Rest) {
   return punctuationIndicatesLineBreak(Line) || isHardLineBreakIndicator(Rest);
 }
 
+static auto getRecordOfMethodDecl(const CXXMethodDecl *Method) {
+  const auto *Record = Method->getParent();
+  if (Record)
+    Record = Record->getDefinition();
+  if (!Record || Record->isInvalidDecl())
+    return static_cast<const CXXRecordDecl *>(nullptr);
+  if (Record->isDependentType()) {
+    for (const auto &Base : Record->bases()) {
+      if (Base.getType()->isDependentType())
+        return static_cast<const CXXRecordDecl *>(nullptr);
+    }
+  }
+  return Record;
+}
+static uint64_t getVirtualCXXMethodsCount(const CXXRecordDecl *Record) {
+  uint64_t Count = 0;
+  for (const auto &Base :
+       Record->bases()) {
+    const auto *BaseRecord = Base.getType()->getAsCXXRecordDecl();
+    if (BaseRecord) {
+      auto VirtualMethodsInBase = getVirtualCXXMethodsCount(BaseRecord);
+      if (VirtualMethodsInBase) {
+        Count += VirtualMethodsInBase;
+        break;
+      }
+    }
+  }
+  static auto IsOverride = [](const CXXMethodDecl *Method,
+                              const CXXRecordDecl *Record) {
+    if (Method->size_overridden_methods() == 0)
+      return false;
+    for (const auto *MD : Method->overridden_methods()) {
+      const auto *RD = getRecordOfMethodDecl(MD);
+      if (RD && Record->isDerivedFrom(RD))
+        return true;
+    }
+    return false;
+  };
+  const auto MSABI =
+      Record->getASTContext().getTargetInfo().getCXXABI().isMicrosoft();
+  for (const auto *MD : Record->methods()) {
+    if (!MD->isVirtual() || IsOverride(MD, Record))
+      continue;
+    if (!MSABI && Record->getDestructor() == MD)
+      Count++;
+    Count++;
+  }
+  return Count;
+}
+static constexpr auto InvalidVirtualCXXMethodId =
+    std::numeric_limits<uint64_t>::max();
+static uint64_t findVirtualCXXMethodId(const CXXMethodDecl *Method,
+                                       const CXXRecordDecl *Record = nullptr) {
+  if (!Method->isVirtual())
+    return InvalidVirtualCXXMethodId;
+  if (!Record) {
+    Record = getRecordOfMethodDecl(Method);
+    if (!Record)
+      return InvalidVirtualCXXMethodId;
+  }
+  const auto MSABI =
+      Record->getASTContext().getTargetInfo().getCXXABI().isMicrosoft();
+  if (Record->getNumBases() == 0) {
+    uint64_t Id = 0;
+    for (const auto *MD : Record->methods()) {
+      if (!MD->isVirtual())
+        continue;
+      if (MD == Method)
+        return Id;
+      if (!MSABI && Record->getDestructor() == MD)
+        Id++;
+      Id++;
+    }
+  } else {
+    static auto GetBaseMethod = [](const CXXMethodDecl *Method,
+                                   const CXXRecordDecl *Record) {
+      const CXXMethodDecl *Base = Method;
+      if (Method->size_overridden_methods() == 0)
+        return Base;
+      for (const auto *MD : Method->overridden_methods()) {
+        const auto *RD = getRecordOfMethodDecl(MD);
+        if (RD && Record->isDerivedFrom(RD)) {
+          Record = RD;
+          Base = MD;
+        }
+      }
+      return Base;
+    };
+    auto GetMethodId = [MSABI](const CXXMethodDecl *Method,
+                               const CXXRecordDecl *Record,
+                               uint64_t IdOffset = 0) {
+      uint64_t Id = IdOffset;
+      for (const auto *MD : Record->methods()) {
+        const auto *BaseMethod = GetBaseMethod(MD, Record);
+        if (!MD->isVirtual() || BaseMethod != MD)
+          continue;
+        if (MD == Method)
+          return Id;
+        if (!MSABI && Record->getDestructor() == MD)
+          Id++;
+        Id++;
+      }
+      return InvalidVirtualCXXMethodId;
+    };
+
+    const auto *BaseMethod = GetBaseMethod(Method, Record);
+    if (BaseMethod != Method) {
+      const auto *Record = BaseMethod->getThisType()->getAsCXXRecordDecl();
+      return findVirtualCXXMethodId(BaseMethod, Record);
+    }
+    for (const auto &Base :
+         Record->bases()) {
+      const auto *BaseRecord = Base.getType()->getAsCXXRecordDecl();
+      if (!BaseRecord)
+        continue;
+      uint64_t IdOffset = getVirtualCXXMethodsCount(BaseRecord);
+      if (IdOffset == 0)
+        continue;
+      return GetMethodId(Method, Record, IdOffset);
+    }
+    return GetMethodId(Method, Record);
+  }
+  return InvalidVirtualCXXMethodId;
+}
+
 void addLayoutInfo(const NamedDecl &ND, HoverInfo &HI) {
   if (ND.isInvalidDecl())
     return;
@@ -1039,6 +1164,18 @@ void addLayoutInfo(const NamedDecl &ND, HoverInfo &HI) {
     }
     return;
   }
+
+  if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(&ND)) {
+    if (MD->isVirtual()) {
+      auto Id = findVirtualCXXMethodId(MD);
+      if (Id != InvalidVirtualCXXMethodId) {
+        const auto PointerSize =
+            Ctx.getTargetInfo().getPointerWidth(LangAS::Default);
+        HI.Offset = Id * PointerSize;
+        HI.Size = PointerSize;
+      }
+    }
+  }
 }
 
 HoverInfo::PassType::PassMode getPassMode(QualType ParmType) {