================
@@ -23,13 +23,217 @@
 #include "llvm/ADT/StringRef.h"
 #include <limits>
 #include <optional>
-#include <tuple>
 
 #define DEBUG_TYPE "FindSymbols"
 
 namespace clang {
 namespace clangd {
 
+namespace {
+SymbolTags toSymbolTagBitmask(const SymbolTag ST) {
+  return (1 << static_cast<unsigned>(ST));
+}
+} // namespace
+
+
+
+// "Static" means many things in C++, only some get the "static" modifier.
+//
+// Meanings that do:
+// - Members associated with the class rather than the instance.
+//   This is what 'static' most often means across languages.
+// - static local variables
+//   These are similarly "detached from their context" by the static keyword.
+//   In practice, these are rarely used inside classes, reducing confusion.
+//
+// Meanings that don't:
+// - Namespace-scoped variables, which have static storage class.
+//   This is implicit, so the keyword "static" isn't so strongly associated.
+//   If we want a modifier for these, "global scope" is probably the concept.
+// - Namespace-scoped variables/functions explicitly marked "static".
+//   There the keyword changes *linkage* , which is a totally different 
concept.
+//   If we want to model this, "file scope" would be a nice modifier.
+//
+// This is confusing, and maybe we should use another name, but because 
"static"
+// is a standard LSP modifier, having one with that name has advantages.
+bool isStatic(const Decl *D) {
+  if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
+    return CMD->isStatic();
+  if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D))
+    return VD->isStaticDataMember() || VD->isStaticLocal();
+  if (const auto *OPD = llvm::dyn_cast<ObjCPropertyDecl>(D))
+    return OPD->isClassProperty();
+  if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D))
+    return OMD->isClassMethod();
+  if (const auto *FD = llvm::dyn_cast<FunctionDecl>(D))
+    return FD->isStatic();
+  return false;
+}
+
+// Whether T is const in a loose sense - is a variable with this type readonly?
+bool isConst(QualType T) {
+  if (T.isNull())
+    return false;
+  T = T.getNonReferenceType();
+  if (T.isConstQualified())
+    return true;
+  if (const auto *AT = T->getAsArrayTypeUnsafe())
+    return isConst(AT->getElementType());
+  if (isConst(T->getPointeeType()))
+    return true;
+  return false;
+}
+
+// Whether D is const in a loose sense (should it be highlighted as such?)
+// FIXME: This is separate from whether *a particular usage* can mutate D.
+//        We may want V in V.size() to be readonly even if V is mutable.
+bool isConst(const Decl *D) {
+  if (llvm::isa<EnumConstantDecl>(D) || llvm::isa<NonTypeTemplateParmDecl>(D))
+    return true;
+  if (llvm::isa<FieldDecl>(D) || llvm::isa<VarDecl>(D) ||
+      llvm::isa<MSPropertyDecl>(D) || llvm::isa<BindingDecl>(D)) {
+    if (isConst(llvm::cast<ValueDecl>(D)->getType()))
+      return true;
+      }
+  if (const auto *OCPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) {
+    if (OCPD->isReadOnly())
+      return true;
+  }
+  if (const auto *MPD = llvm::dyn_cast<MSPropertyDecl>(D)) {
+    if (!MPD->hasSetter())
+      return true;
+  }
+  if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
+    if (CMD->isConst())
+      return true;
+  }
+  if (const auto *FD = llvm::dyn_cast<FunctionDecl>(D))
+    return isConst(FD->getReturnType());
+  return false;
+}
+
+// Indicates whether declaration D is abstract in cases where D is a struct or 
a
+// class.
+bool isAbstract(const Decl *D) {
+  if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
+    return CMD->isPureVirtual();
+  if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D))
+    return CRD->hasDefinition() && CRD->isAbstract();
+  return false;
+}
+
+// Indicates whether declaration D is virtual in cases where D is a method.
+bool isVirtual(const Decl *D) {
+  if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
+    return CMD->isVirtual();
+  return false;
+}
+
+// Indicates whether declaration D is final in cases where D is a struct, class
+// or method.
+bool isFinal(const Decl *D) {
+  if (const auto *CRD = dyn_cast<CXXMethodDecl>(D))
+    return CRD->hasAttr<FinalAttr>();
+
+  if (const auto *CRD = dyn_cast<CXXRecordDecl>(D))
+    return CRD->hasAttr<FinalAttr>();
+
+  return false;
+}
+
+
+bool isUniqueDefinition(const NamedDecl *Decl) {
+  if (auto *Func = dyn_cast<FunctionDecl>(Decl))
+    return Func->isThisDeclarationADefinition();
+  if (auto *Klass = dyn_cast<CXXRecordDecl>(Decl))
+    return Klass->isThisDeclarationADefinition();
+  if (auto *Iface = dyn_cast<ObjCInterfaceDecl>(Decl))
+    return Iface->isThisDeclarationADefinition();
+  if (auto *Proto = dyn_cast<ObjCProtocolDecl>(Decl))
+    return Proto->isThisDeclarationADefinition();
+  if (auto *Var = dyn_cast<VarDecl>(Decl))
+    return Var->isThisDeclarationADefinition();
+  return isa<TemplateTypeParmDecl>(Decl) ||
+         isa<NonTypeTemplateParmDecl>(Decl) ||
+         isa<TemplateTemplateParmDecl>(Decl) || isa<ObjCCategoryDecl>(Decl) ||
+         isa<ObjCImplDecl>(Decl);
+}
+
+// Backwards-compatible default behavior: determine whether this NamedDecl is
+// definition based on `isUniqueDefinition`, assuming that ND is a declaration.
+SymbolTags computeSymbolTags(const NamedDecl &ND) {
+  return computeSymbolTags(ND, true);
+}
+
+SymbolTags computeSymbolTags(const NamedDecl &ND, bool IsDecl) {
+  SymbolTags Result = 0;
+  const auto IsDef = isUniqueDefinition(&ND);
+
+  if (ND.isDeprecated())
+    Result |= toSymbolTagBitmask(SymbolTag::Deprecated);
+
+  if (isConst(&ND))
+    Result |= toSymbolTagBitmask(SymbolTag::ReadOnly);
+
+  if (isStatic(&ND))
+    Result |= toSymbolTagBitmask(SymbolTag::Static);
+
+  if (isVirtual(&ND))
+    Result |= toSymbolTagBitmask(SymbolTag::Virtual);
+
+  if (isAbstract(&ND))
+    Result |= toSymbolTagBitmask(SymbolTag::Abstract);
+
+  if (isFinal(&ND))
+    Result |= toSymbolTagBitmask(SymbolTag::Final);
+
+  if (IsDecl && not isa<UnresolvedUsingValueDecl>(ND)) {
+    // Do not treat an UnresolvedUsingValueDecl as a declaration.
+    // It's more common to think of it as a reference to the
+    // underlying declaration.
+    Result |= toSymbolTagBitmask(SymbolTag::Declaration);
----------------
ratzdi wrote:

In` SemanticHighlighting.cpp` (main branch Line 1132) there is an if-statement 
that I have kept (otherwise breaking tests) in my work (without precise 
knowledge of the purpose of the condition)

```c++
if (R.IsDecl) {
  // Do not treat an UnresolvedUsingValueDecl as a declaration.
  // It's more common to think of it as a reference to the
  // underlying declaration.
  if (!isa<UnresolvedUsingValueDecl>(Decl))
    Tok.addModifier(HighlightingModifier::Declaration);
  if (isUniqueDefinition(Decl))
    Tok.addModifier(HighlightingModifier::Definition);
}
``` 

now the if-statement is located in computeSymbolTags
`const auto SymbolTags = computeSymbolTags(*Decl, R.IsDecl);`

and the lines

```c++
...

const auto SymbolTags = computeSymbolTags(*Decl, R.IsDecl);

...

if (SymbolTags & toSymbolTagBitmask(SymbolTag::Declaration))
  Tok.addModifier(HighlightingModifier::Declaration);

if (SymbolTags & toSymbolTagBitmask(SymbolTag::Definition))
  Tok.addModifier(HighlightingModifier::Definition);

...
```

replacing the code block, mentioned above.



> The [wording in the 
> PR](https://github.com/microsoft/language-server-protocol/pull/2003/changes#diff-9403073ad34251b3ce4b6442973eb6d78dd5b61aeda57a12a0a8126be7c2fadcR269-R279)
>  -- specifically "Render a symbol as definition (in contrast to declaration)" 
> -- makes me think perhaps Declaration should only be set for things that are 
> not a Definition?

When we decide to either ... or, some tests will break.

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

Reply via email to