From 3ac550984e83c2478772c800b1f1b5cffd63a10d Mon Sep 17 00:00:00 2001 From: ruturaj4 Date: Thu, 13 Apr 2023 13:48:30 +0100 Subject: [PATCH] [clang][ExtractAPI] Complete declaration fragments for TagDecl types defined in a typedef MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit enums and structs declared inside typedefs have incorrect declaration fragments, where the typedef keyword and other syntax is missing. For the following struct: typedef struct Test { int hello; } Test; The produced declaration is: "declarationFragments": [ { "kind": "keyword", "spelling": "struct" }, { "kind": "text", "spelling": " " }, { "kind": "identifier", "spelling": "Test" } ], instead the declaration fragments should represent the following typedef struct Test { … } Test; This patch removes the condition in SymbolGraphSerializer.cpp file and completes declaration fragments Reviewed By: dang Differential Revision: https://reviews.llvm.org/D146385 --- .../clang/ExtractAPI/DeclarationFragments.h | 19 + clang/include/clang/ExtractAPI/ExtractAPIVisitor.h | 34 ++ clang/test/ExtractAPI/typedef_struct_enum.c | 441 +++++++++++++++++++++ 3 files changed, 494 insertions(+) create mode 100644 clang/test/ExtractAPI/typedef_struct_enum.c diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h index a5db4d2..90121a1 100644 --- a/clang/include/clang/ExtractAPI/DeclarationFragments.h +++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h @@ -99,6 +99,25 @@ public: const std::vector &getFragments() const { return Fragments; } + // Add a new Fragment to the beginning of the Fragments. + DeclarationFragments &appendFront(StringRef Spelling, FragmentKind Kind, + StringRef PreciseIdentifier = "", + const Decl *Declaration = nullptr) { + Fragments.emplace(Fragments.begin(), Spelling, Kind, PreciseIdentifier, + Declaration); + return *this; + } + + DeclarationFragments &appendFront(DeclarationFragments &&Other) { + Fragments.insert(Fragments.begin(), + std::make_move_iterator(Other.Fragments.begin()), + std::make_move_iterator(Other.Fragments.end())); + Other.Fragments.clear(); + return *this; + } + + void removeLast() { Fragments.pop_back(); } + /// Append a new Fragment to the end of the Fragments. /// /// \returns a reference to the DeclarationFragments object itself after diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h index a31648b..57f2c41 100644 --- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h +++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h @@ -22,6 +22,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/ExtractAPI/API.h" #include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h" +#include "llvm/ADT/StringRef.h" #include namespace clang { @@ -105,6 +106,24 @@ private: } }; +template +static void modifyRecords(const T &Records, const StringRef &Name) { + for (const auto &Record : Records) { + if (Name == Record.second.get()->Name) { + Record.second.get()->Declaration.removeLast(); + Record.second.get() + ->Declaration + .appendFront(" ", DeclarationFragments::FragmentKind::Text) + .appendFront("typedef", DeclarationFragments::FragmentKind::Keyword, + "", nullptr) + .append(" { ... } ", DeclarationFragments::FragmentKind::Text) + .append(Name, DeclarationFragments::FragmentKind::Identifier) + .append(";", DeclarationFragments::FragmentKind::Text); + break; + } + } +} + template bool ExtractAPIVisitorBase::VisitVarDecl(const VarDecl *Decl) { // skip function parameters. @@ -401,6 +420,21 @@ bool ExtractAPIVisitorBase::VisitTypedefNameDecl( if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) return true; + // Add the notion of typedef for tag type (struct or enum) of the same name. + if (const ElaboratedType *ET = + dyn_cast(Decl->getUnderlyingType())) { + if (const TagType *TagTy = dyn_cast(ET->desugar())) { + if (Decl->getName() == TagTy->getDecl()->getName()) { + if (TagTy->getDecl()->isStruct()) { + modifyRecords(API.getStructs(), Decl->getName()); + } + if (TagTy->getDecl()->isEnum()) { + modifyRecords(API.getEnums(), Decl->getName()); + } + } + } + } + PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); StringRef Name = Decl->getName(); diff --git a/clang/test/ExtractAPI/typedef_struct_enum.c b/clang/test/ExtractAPI/typedef_struct_enum.c new file mode 100644 index 0000000..b3648ae --- /dev/null +++ b/clang/test/ExtractAPI/typedef_struct_enum.c @@ -0,0 +1,441 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang -extract-api -target arm64-apple-macosx \ +// RUN: %t/input.h -o %t/output.json | FileCheck -allow-empty %s + +// Generator version is not consistent across test runs, normalize it. +// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \ +// RUN: %t/output.json >> %t/output-normalized.json +// RUN: diff %t/reference.output.json %t/output-normalized.json + +// CHECK-NOT: error: +// CHECK-NOT: warning: + +//--- input.h +typedef struct Test { +} Test; + +typedef enum Test2 { + simple +} Test2; + +struct Foo; +typedef struct Foo TypedefedFoo; +struct Foo { + int bar; +}; + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [ + { + "kind": "memberOf", + "source": "c:@E@Test2@simple", + "target": "c:@E@Test2", + "targetFallback": "Test2" + }, + { + "kind": "memberOf", + "source": "c:@S@Foo@FI@bar", + "target": "c:@S@Foo", + "targetFallback": "Foo" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "typedef" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "keyword", + "spelling": "enum" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Test2" + }, + { + "kind": "text", + "spelling": ": " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:i", + "spelling": "unsigned int" + }, + { + "kind": "text", + "spelling": " { ... } " + }, + { + "kind": "identifier", + "spelling": "Test2" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@E@Test2" + }, + "kind": { + "displayName": "Enumeration", + "identifier": "c.enum" + }, + "location": { + "position": { + "character": 14, + "line": 4 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Test2" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Test2" + } + ], + "title": "Test2" + }, + "pathComponents": [ + "Test2" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "identifier", + "spelling": "simple" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@E@Test2@simple" + }, + "kind": { + "displayName": "Enumeration Case", + "identifier": "c.enum.case" + }, + "location": { + "position": { + "character": 3, + "line": 5 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "simple" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "simple" + } + ], + "title": "simple" + }, + "pathComponents": [ + "Test2", + "simple" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "typedef" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "keyword", + "spelling": "struct" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Test" + }, + { + "kind": "text", + "spelling": " { ... } " + }, + { + "kind": "identifier", + "spelling": "Test" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@S@Test" + }, + "kind": { + "displayName": "Structure", + "identifier": "c.struct" + }, + "location": { + "position": { + "character": 16, + "line": 1 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Test" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Test" + } + ], + "title": "Test" + }, + "pathComponents": [ + "Test" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "struct" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@S@Foo" + }, + "kind": { + "displayName": "Structure", + "identifier": "c.struct" + }, + "location": { + "position": { + "character": 8, + "line": 10 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "Foo" + }, + "pathComponents": [ + "Foo" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "bar" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:@S@Foo@FI@bar" + }, + "kind": { + "displayName": "Instance Property", + "identifier": "c.property" + }, + "location": { + "position": { + "character": 9, + "line": 11 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "bar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "bar" + } + ], + "title": "bar" + }, + "pathComponents": [ + "Foo", + "bar" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "typedef" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "keyword", + "spelling": "struct" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:@S@Foo", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "TypedefedFoo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c", + "precise": "c:input.h@T@TypedefedFoo" + }, + "kind": { + "displayName": "Type Alias", + "identifier": "c.typealias" + }, + "location": { + "position": { + "character": 20, + "line": 9 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "TypedefedFoo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "TypedefedFoo" + } + ], + "title": "TypedefedFoo" + }, + "pathComponents": [ + "TypedefedFoo" + ], + "type": "c:@S@Foo" + } + ] +} -- 2.7.4