diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h --- a/clang/include/clang/ExtractAPI/DeclarationFragments.h +++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h @@ -99,6 +99,23 @@ 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; + } + /// Append a new Fragment to the end of the Fragments. /// /// \returns a reference to the DeclarationFragments object itself after diff --git a/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp b/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp --- a/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp @@ -338,6 +338,41 @@ if (!LocationChecker(Decl->getLocation())) 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()) { + for (const auto &Struct : API.getStructs()) { + if (Decl->getName() == Struct.second.get()->Name) { + Struct.second.get() + ->Declaration + .appendFront(" ", DeclarationFragments::FragmentKind::Text) + .appendFront("typedef", + DeclarationFragments::FragmentKind::Keyword, "", + nullptr); + break; + } + } + } + if (TagTy->getDecl()->isEnum()) { + for (const auto &Enum : API.getEnums()) { + if (Decl->getName() == Enum.second.get()->Name) { + Enum.second.get() + ->Declaration + .appendFront(" ", DeclarationFragments::FragmentKind::Text) + .appendFront("typedef", + DeclarationFragments::FragmentKind::Keyword, "", + nullptr); + break; + } + } + } + } + } + } + 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 --- /dev/null +++ b/clang/test/ExtractAPI/typedef_struct_enum.c @@ -0,0 +1,230 @@ +// 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; + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [ + { + "kind": "memberOf", + "source": "c:@E@Test2@simple", + "target": "c:@E@Test2", + "targetFallback": "Test2" + } + ], + "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": ";" + } + ], + "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": ";" + } + ], + "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" + ] + } + ] +}