sammccall added a comment.

Thanks, this looks pretty good, I think there are a couple of adjustments...



================
Comment at: clang-tools-extra/clangd/XRefs.cpp:276
        getDeclAtPosition(AST, CurLoc, Relations, NodeKind)) {
     // Special case: void foo() ^override: jump to the overridden method.
     if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
----------------
dgoldman wrote:
> sammccall wrote:
> > dgoldman wrote:
> > > sammccall wrote:
> > > > dgoldman wrote:
> > > > > sammccall wrote:
> > > > > > dgoldman wrote:
> > > > > > > Think it would make sense to special case ObjCInterfaceDecl here 
> > > > > > > to get at both the interface definition + implementation if 
> > > > > > > available?
> > > > > > Rather than returning both results, I think it's more consistent to 
> > > > > > return them as a declaration/definition pair.
> > > > > > 
> > > > > > (This means special-casing ObjCImplDecl in namedDecl or at least 
> > > > > > getDeclAsPosition, so you always end up with the ObjCInterfaceDecl 
> > > > > > instead)
> > > > > Whoops, meant to comment here but it was lost. I'm not sure what you 
> > > > > meant here. Should this be done here inside the for loop or in 
> > > > > getDeclAtPosition?
> > > > > 
> > > > > Are you saying that given:
> > > > > 
> > > > > ```
> > > > > @interface Foo // A
> > > > > @end
> > > > > @implementation Foo // B
> > > > > @end
> > > > > ```
> > > > > B --> A here
> > > > > 
> > > > > and similarly
> > > > > 
> > > > > ```
> > > > > @interface Foo // A
> > > > > @end
> > > > > @interface Foo (Ext) // B
> > > > > @end
> > > > > @implementation Foo (Ext) // C
> > > > > @end
> > > > > ```
> > > > > B --> A
> > > > > C --> B (and A? it's unclear how this should map over, e.g. maybe Foo 
> > > > > loc --> A, Ext --> B)
> > > > In the first example, selecting either A or B should yield one 
> > > > LocatedSymbol with {Decl=A, Def=B}. This shouldn't require any 
> > > > special-casing.
> > > > 
> > > > The second example is very similar to template specialization, with 
> > > > exceptions:
> > > >  - there's always a Decl/Def pair you may want to navigate between, 
> > > > whereas in templates there rarely is, so we have ambiguity
> > > >  - there's no AST like there is for template names and args, just a bag 
> > > > of tokens
> > > > 
> > > > I'd suggest, given `@interface Foo (Ext)`:
> > > >  - we produce a LocatedSymbol with {Decl=@interface Foo(Ext), 
> > > > Def=@implementation  Foo(Ext)} - this is the default behavior
> > > >  - if the cursor is exactly on the token `Foo`, we also produce a 
> > > > LocatedSymbol with {Decl=@interface Foo, Def=@implementation Foo} - 
> > > > this is similar to the template special case
> > > >  - if the cursor is exactly on the token Ext... are categories 
> > > > explicitly/separately declared anywhere? I guess not. If they are, we 
> > > > could special case this too.
> > > > And `@implementation Foo(Ext)` should behave in exactly the same way.
> > > Trying this out now, two problems:
> > > 
> > > - getDeclAtPosition will call findTarget. Due to the changes above we map 
> > > `ObjCCategoryImplDecl` to its interface. This is OK but then when we 
> > > check for the loc it's obviously != to the impl's loc. Should I modify 
> > > this to check the contents of the loc for equality?
> > > 
> > > - We call `getDeclAtPosition` only looking for 
> > > DeclRelation::TemplatePattern | DeclRelation::Alias, but this is actually 
> > > a DeclRelation::Underlying. If I don't add that we filter out the 
> > > ObjCCategoryImplDecl. If I add it we get a failure in 
> > > LocateSymbol.TemplateTypedef (we now find another symbol, not sure what 
> > > is intended here)
> > > 
> > > 
> > > getDeclAtPosition will call findTarget. Due to the changes above we map 
> > > ObjCCategoryImplDecl to its interface. This is OK but then when we check 
> > > for the loc it's obviously != to the impl's loc. Should I modify this to 
> > > check the contents of the loc for equality?
> > 
> > Oh right... yes, this seems fine to me (for the ObjCContainerDecl 
> > subclasses only, and with a comment) 
> > 
> > > We call getDeclAtPosition only looking for DeclRelation::TemplatePattern 
> > > | DeclRelation::Alias, but this is actually a DeclRelation::Underlying. 
> > > If I don't add that we filter out the ObjCCategoryImplDecl. If I add it 
> > > we get a failure in LocateSymbol.TemplateTypedef (we now find another 
> > > symbol, not sure what is intended here)
> > 
> > I'm not sure what "this" in "this is actually a DeclRelation::Underlying" 
> > refers to. Do you have a failing testcase?
> > if the cursor is exactly on the token Ext... are categories 
> > explicitly/separately declared anywhere? I guess not. If they are, we could 
> > special case this too.
> 
> Not besides what was mentioned above, so I think it's OK at the moment.
> 
> > I'm not sure what "this" in "this is actually a DeclRelation::Underlying" 
> > refers to. Do you have a failing testcase?
> 
> I meant that for ObjC, I used `DeclRelation::Underlying` so I have to include 
> it to get the `ObjCCategoryImplDecl`. I guess I can later filter out the 
> `DeclRelation::Underlying` unless it's for the ObjC case.
> 
> Failing test, I think only returning callback is intentional?
> 
> ```
>  LocateSymbol.TemplateTypedefs
> 
> Value of: locateSymbolAt(AST, T.point())
> Expected: has 1 element that sym "callback"
>   Actual: { function: 1:30-1:38@file:///clangd-test/TestTU.cpp 
> def=1:30-1:38@file:///clangd-test/TestTU.cpp, callback: 
> 2:29-2:37@file:///clangd-test/TestTU.cpp }, which has 2 elements
> 
> Code:
>     template <class T> struct function {};
>     template <class T> using callback = function<T()>;
> 
>     c^allback<int> foo;
> ```
> 
> 
> 
> 
> I meant that for ObjC, I used DeclRelation::Underlying

`Underlying` isn't appropriate here - it's used for situations like typedefs 
where there's clearly two symbols (two names) and an asymmetrical synonym 
relation (e.g. int32 is a synonym for int, but not the other way around).
Here we have a declaration/definition pair of a (what we're treating as) a 
single decl.

Can you just stop setting `Underlying` in FindTargets?


================
Comment at: clang-tools-extra/clangd/index/SymbolCollector.cpp:160
 
-// Checks whether \p ND is a definition of a TagDecl (class/struct/enum/union)
-// in a header file, in which case clangd would prefer to use ND as a canonical
-// declaration.
+// Checks whether \p ND is a preferred declaration to clangd. Typically this
+// means preferring true declarations over forward declarations.
----------------
"preferred" declaration is just the method name, and "true" declaration doesn't 
seem to clarify much.

For this overview paragraph I'd suggest: Checks whether \p ND is a good 
candidate to be the *canonical* declaration of its symbol (e.g. a 
go-to-declaration target). This overrides the default of using Clang's 
canonical declaration, which is the first in the TU.


================
Comment at: clang-tools-extra/clangd/index/SymbolCollector.cpp:163
+//
+// For C++:
+// If \p ND is a definition of a TagDecl (class/struct/enum/union) in a header,
----------------
I'm not sure the for C++/for ObjC split in the doc makes sense - these special 
cases are more to do with the node types than the underlying languages (which 
can overlap).


================
Comment at: clang-tools-extra/clangd/index/SymbolCollector.cpp:186
+//
+// For example, Objective-C classes can have three types of
+// declarations, all with the same USR:
----------------
this isn't really an example, it's the only case. I think it can be moved 
inside the function, above the case itself.

"We treat ObjCImplDecl as a definition but not a declaration - the 
corresponding ObjcInterfaceDecl is the declaration"


================
Comment at: clang-tools-extra/clangd/index/SymbolCollector.cpp:371
+  // TU, because in practice they are definitions.
+  if (shouldRecordDeclaration(*ND)) {
+    if (isPreferredDeclaration(*OriginalDecl, Roles))
----------------
Hmm, not sure about this. What if the @implementation is in a different file 
we're not indexing? Then we'll end up with no declaration.

Probably instead we should treat this as if it were a non-preferred 
non-canonical declaration whose canonical declaration is the @interface.
i.e. around where FOK is handled:
```
if (auto *OCID = llvm::dyn_cast<ObjCImplDecl>(D))
  D = OCID->getClassInterface(); // hmm, but maybe not if it's an implicit 
@interface
```


================
Comment at: clang-tools-extra/clangd/index/SymbolCollector.cpp:376
+    else
+      BasicSymbol = addDeclaration(*ND, std::move(*ID), IsMainFileOnly);
+  }
----------------
you're now clobbering BasicSymbol every time, even if it was set and this isn't 
a preferred declaration


================
Comment at: clang-tools-extra/clangd/unittests/FindTargetTests.cpp:611
 
+  Code = R"cpp(
+    @interface Foo
----------------
can we add a case here and in symbolcollector testing the "implicit interface 
decl" case? (ObjCInterfaceDecl::isImplicitInterfaceDecl())


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83501/new/

https://reviews.llvm.org/D83501



_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to