diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -238,6 +238,9 @@ /// The extra two bits are used for the ModuleOwnershipKind. llvm::PointerIntPair NextInContextAndBits; + /// The previous declaration within the same lexical DeclContext. + Decl *PreviousInContext; + private: friend class DeclContext; @@ -374,21 +377,23 @@ protected: Decl(Kind DK, DeclContext *DC, SourceLocation L) : NextInContextAndBits(nullptr, getModuleOwnershipKindForChildOf(DC)), - DeclCtx(DC), Loc(L), DeclKind(DK), InvalidDecl(false), HasAttrs(false), - Implicit(false), Used(false), Referenced(false), - TopLevelDeclInObjCContainer(false), Access(AS_none), FromASTFile(0), - IdentifierNamespace(getIdentifierNamespaceForKind(DK)), + PreviousInContext(nullptr), DeclCtx(DC), Loc(L), DeclKind(DK), + InvalidDecl(false), HasAttrs(false), Implicit(false), Used(false), + Referenced(false), TopLevelDeclInObjCContainer(false), Access(AS_none), + FromASTFile(0), IdentifierNamespace(getIdentifierNamespaceForKind(DK)), CacheValidAndLinkage(0) { - if (StatisticsEnabled) add(DK); + if (StatisticsEnabled) + add(DK); } Decl(Kind DK, EmptyShell Empty) - : DeclKind(DK), InvalidDecl(false), HasAttrs(false), Implicit(false), - Used(false), Referenced(false), TopLevelDeclInObjCContainer(false), - Access(AS_none), FromASTFile(0), + : PreviousInContext(nullptr), DeclKind(DK), InvalidDecl(false), + HasAttrs(false), Implicit(false), Used(false), Referenced(false), + TopLevelDeclInObjCContainer(false), Access(AS_none), FromASTFile(0), IdentifierNamespace(getIdentifierNamespaceForKind(DK)), CacheValidAndLinkage(0) { - if (StatisticsEnabled) add(DK); + if (StatisticsEnabled) + add(DK); } virtual ~Decl(); @@ -431,6 +436,9 @@ Decl *getNextDeclInContext() { return NextInContextAndBits.getPointer(); } const Decl *getNextDeclInContext() const {return NextInContextAndBits.getPointer();} + Decl *getPreviousDeclInContext() { return PreviousInContext; } + const Decl *getPreviousDeclInContext() const { return PreviousInContext; } + DeclContext *getDeclContext() { if (isInSemaDC()) return getSemanticDC(); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -258,6 +258,44 @@ CommentBeforeDecl->isTrailingComment()) return nullptr; + const SourceLocation &CommentEndLoc = CommentBeforeDecl->getEndLoc(); + if (CommentEndLoc.isInvalid()) + return nullptr; + + // Check if there's any other declaration in between the end of the comment + // and the current declaration. + if (const Decl *PreviousInContext = D->getPreviousDeclInContext()) { + // Use the previous decl's declaration location if the previous declaration + // is the lexical declaration context of the current declaration, and the + // end location otherwise. For example: + // + // ``` + // /// Doc for struct s + // struct s { // <- declaration location for `s` + // + // int i; // Both the PreviousDeclInContext and the + // // LexicalDeclContext of `i` is `s` + // + // /// Doc for func + // void func(); + // }; // <- end location for `s` + // + // bool b; // The PreviousDeclInContext of `b` is `s` + // ``` + // + // In this case, `Doc for struct s` shouldn't be attached to `i`; and + // `Doc for func` shouldn't be attached to `b`. + const DeclContext *DC = dyn_cast(PreviousInContext); + const SourceLocation &PreviousDeclLoc = + DC && D->getLexicalDeclContext() == DC + ? PreviousInContext->getLocation() + : PreviousInContext->getEndLoc(); + if (PreviousDeclLoc.isValid() && PreviousDeclLoc.isFileID() && + SourceMgr.isPointWithin(PreviousDeclLoc, CommentEndLoc, + RepresentativeLocForDecl)) + return nullptr; + } + // Decompose the end of the comment. const unsigned CommentEndOffset = Comments.getCommentEndOffset(CommentBeforeDecl); @@ -273,9 +311,8 @@ StringRef Text(Buffer + CommentEndOffset, DeclLocDecomp.second - CommentEndOffset); - // There should be no other declarations or preprocessor directives between - // comment and declaration. - if (Text.find_first_of(";{}#@") != StringRef::npos) + // There should be no preprocessor directives between comment and declaration. + if (Text.find_first_of("#@") != StringRef::npos) return nullptr; return CommentBeforeDecl; diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1344,6 +1344,7 @@ else FirstNewDecl = D; + D->PreviousInContext = PrevDecl; PrevDecl = D; } @@ -1392,6 +1393,8 @@ std::tie(ExternalFirst, ExternalLast) = BuildDeclChain(Decls, FieldsAlreadyLoaded); ExternalLast->NextInContextAndBits.setPointer(FirstDecl); + if (FirstDecl) + FirstDecl->PreviousInContext = ExternalLast; FirstDecl = ExternalFirst; if (!LastDecl) LastDecl = ExternalLast; @@ -1498,25 +1501,26 @@ assert((D->NextInContextAndBits.getPointer() || D == LastDecl) && "decl is not in decls list"); - // Remove D from the decl chain. This is O(n) but hopefully rare. + // Remove D from the decl chain. if (D == FirstDecl) { if (D == LastDecl) FirstDecl = LastDecl = nullptr; else FirstDecl = D->NextInContextAndBits.getPointer(); - } else { - for (Decl *I = FirstDecl; true; I = I->NextInContextAndBits.getPointer()) { - assert(I && "decl not found in linked list"); - if (I->NextInContextAndBits.getPointer() == D) { - I->NextInContextAndBits.setPointer(D->NextInContextAndBits.getPointer()); - if (D == LastDecl) LastDecl = I; - break; - } - } + } + + if (auto *Next = D->NextInContextAndBits.getPointer()) + Next->PreviousInContext = D->PreviousInContext; + + if (auto *Prev = D->PreviousInContext) { + Prev->NextInContextAndBits.setPointer(D->NextInContextAndBits.getPointer()); + if (D == LastDecl) + LastDecl = Prev; } // Mark that D is no longer in the decl chain. D->NextInContextAndBits.setPointer(nullptr); + D->PreviousInContext = nullptr; // Remove D from the lookup table if necessary. if (isa(D)) { @@ -1551,6 +1555,7 @@ if (FirstDecl) { LastDecl->NextInContextAndBits.setPointer(D); + D->PreviousInContext = LastDecl; LastDecl = D; } else { FirstDecl = LastDecl = D; diff --git a/clang/test/Index/annotate-comments-enum-constant.c b/clang/test/Index/annotate-comments-enum-constant.c new file mode 100644 --- /dev/null +++ b/clang/test/Index/annotate-comments-enum-constant.c @@ -0,0 +1,16 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng %s > %t/out +// RUN: FileCheck %s --dump-input always < %t/out + +enum { + /// Documentation for Foo + Foo, + Bar, // No documentation for Bar + /// Documentation for Baz + Baz, +}; +// CHECK: EnumConstantDecl=Foo:[[@LINE-5]]:3 (Definition) {{.*}} BriefComment=[Documentation for Foo] FullCommentAsHTML=[

Documentation for Foo

] FullCommentAsXML=[Fooc:@Ea@Foo@FooFoo Documentation for Foo] +// CHECK: EnumConstantDecl=Bar:[[@LINE-5]]:3 (Definition) +// CHECK-NOT: BriefComment=[Documentation for Foo] +// CHECK: EnumConstantDecl=Baz:[[@LINE-5]]:3 (Definition) {{.*}} BriefComment=[Documentation for Baz] FullCommentAsHTML=[

Documentation for Baz

] FullCommentAsXML=[Bazc:@Ea@Foo@BazBaz Documentation for Baz] diff --git a/clang/test/Index/annotate-comments.cpp b/clang/test/Index/annotate-comments.cpp --- a/clang/test/Index/annotate-comments.cpp +++ b/clang/test/Index/annotate-comments.cpp @@ -254,6 +254,14 @@ MYMAC(0,0) void isdoxy54(int); +/// IS_DOXYGEN_SINGLE +struct isdoxy55 { + int notdoxy48; + /// IS_DOXYGEN_SINGLE + int isdoxy56; +}; +bool notdoxy49; + #endif // RUN: rm -rf %t @@ -337,3 +345,6 @@ // CHECK: annotate-comments.cpp:241:6: FunctionDecl=isdoxy52:{{.*}} BriefComment=[Aaa. IS_DOXYGEN_START Bbb.] // CHECK: annotate-comments.cpp:248:6: FunctionDecl=isdoxy53:{{.*}} BriefComment=[Aaa. IS_DOXYGEN_START IS_DOXYGEN_END] // CHECK: annotate-comments.cpp:255:6: FunctionDecl=isdoxy54:{{.*}} BriefComment=[Aaa. IS_DOXYGEN_START IS_DOXYGEN_END] + +// CHECK: annotate-comments.cpp:258:8: StructDecl=isdoxy55:{{.*}} IS_DOXYGEN_SINGLE +// CHECK: annotate-comments.cpp:261:7: FieldDecl=isdoxy56:{{.*}} IS_DOXYGEN_SINGLE