diff options
Diffstat (limited to 'utils/TableGen/ClangAttrEmitter.cpp')
-rw-r--r-- | utils/TableGen/ClangAttrEmitter.cpp | 407 |
1 files changed, 203 insertions, 204 deletions
diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index b6d2988964b4..70ce15f5a24e 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -56,9 +56,9 @@ public: V(Spelling.getValueAsString("Variety")), N(Spelling.getValueAsString("Name")) { - assert(V != "GCC" && "Given a GCC spelling, which means this hasn't been" - "flattened!"); - if (V == "CXX11" || V == "Pragma") + assert(V != "GCC" && V != "Clang" && + "Given a GCC spelling, which means this hasn't been flattened!"); + if (V == "CXX11" || V == "C2x" || V == "Pragma") NS = Spelling.getValueAsString("Namespace"); bool Unset; K = Spelling.getValueAsBitOrUnset("KnownToGCC", Unset); @@ -78,11 +78,15 @@ GetFlattenedSpellings(const Record &Attr) { std::vector<FlattenedSpelling> Ret; for (const auto &Spelling : Spellings) { - if (Spelling->getValueAsString("Variety") == "GCC") { + StringRef Variety = Spelling->getValueAsString("Variety"); + StringRef Name = Spelling->getValueAsString("Name"); + if (Variety == "GCC") { // Gin up two new spelling objects to add into the list. - Ret.emplace_back("GNU", Spelling->getValueAsString("Name"), "", true); - Ret.emplace_back("CXX11", Spelling->getValueAsString("Name"), "gnu", - true); + Ret.emplace_back("GNU", Name, "", true); + Ret.emplace_back("CXX11", Name, "gnu", true); + } else if (Variety == "Clang") { + Ret.emplace_back("GNU", Name, "", false); + Ret.emplace_back("CXX11", Name, "clang", false); } else Ret.push_back(FlattenedSpelling(*Spelling)); } @@ -490,6 +494,17 @@ namespace { OS << "}\n"; } + void writeASTVisitorTraversal(raw_ostream &OS) const override { + StringRef Name = getUpperName(); + OS << " if (A->is" << Name << "Expr()) {\n" + << " if (!getDerived().TraverseStmt(A->get" << Name << "Expr()))\n" + << " return false;\n" + << " } else if (auto *TSI = A->get" << Name << "Type()) {\n" + << " if (!getDerived().TraverseTypeLoc(TSI->getTypeLoc()))\n" + << " return false;\n" + << " }\n"; + } + void writeCloneArgs(raw_ostream &OS) const override { OS << "is" << getLowerName() << "Expr, is" << getLowerName() << "Expr ? static_cast<void*>(" << getLowerName() @@ -630,6 +645,10 @@ namespace { << "A->" << getLowerName() << "_size()"; } + void writeASTVisitorTraversal(raw_ostream &OS) const override { + // FIXME: Traverse the elements. + } + void writeCtorBody(raw_ostream &OS) const override { OS << " std::copy(" << getUpperName() << ", " << getUpperName() << " + " << ArgSizeName << ", " << ArgName << ");\n"; @@ -1153,6 +1172,12 @@ namespace { OS << " }"; } + void writeASTVisitorTraversal(raw_ostream &OS) const override { + OS << " if (auto *TSI = A->get" << getUpperName() << "Loc())\n"; + OS << " if (!getDerived().TraverseTypeLoc(TSI->getTypeLoc()))\n"; + OS << " return false;\n"; + } + void writeTemplateInstantiationArgs(raw_ostream &OS) const override { OS << "A->get" << getUpperName() << "Loc()"; } @@ -1305,7 +1330,7 @@ writePrettyPrintFunction(Record &R, if (Variety == "GNU") { Prefix = " __attribute__(("; Suffix = "))"; - } else if (Variety == "CXX11") { + } else if (Variety == "CXX11" || Variety == "C2x") { Prefix = " [["; Suffix = "]]"; std::string Namespace = Spellings[I].nameSpace(); @@ -1419,7 +1444,7 @@ static void writeAttrAccessorDefinition(const Record &R, raw_ostream &OS) { assert(!SpellingList.empty() && "Attribute with empty spelling list can't have accessors!"); for (const auto *Accessor : Accessors) { - std::string Name = Accessor->getValueAsString("Name"); + const StringRef Name = Accessor->getValueAsString("Name"); std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Accessor); OS << " bool " << Name << "() const { return SpellingListIndex == "; @@ -1568,7 +1593,7 @@ struct AttributeSubjectMatchRule { // Abstract rules are used only for sub-rules bool isAbstractRule() const { return getSubjects().empty(); } - std::string getName() const { + StringRef getName() const { return (Constraint ? Constraint : MetaSubject)->getValueAsString("Name"); } @@ -1800,13 +1825,11 @@ PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, // Generate a function that constructs a set of matching rules that describe // to which declarations the attribute should apply to. std::string FnName = "matchRulesFor" + Attr.getName().str(); - std::stringstream SS; - SS << "static void " << FnName << "(llvm::SmallVectorImpl<std::pair<" + OS << "static void " << FnName << "(llvm::SmallVectorImpl<std::pair<" << AttributeSubjectMatchRule::EnumName << ", bool>> &MatchRules, const LangOptions &LangOpts) {\n"; if (Attr.isValueUnset("Subjects")) { - SS << "}\n\n"; - OS << SS.str(); + OS << "}\n\n"; return FnName; } const Record *SubjectObj = Attr.getValueAsDef("Subjects"); @@ -1819,24 +1842,23 @@ PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr, // The rule might be language specific, so only subtract it from the given // rules if the specific language options are specified. std::vector<Record *> LangOpts = Rule.getLangOpts(); - SS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue() + OS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue() << ", /*IsSupported=*/"; if (!LangOpts.empty()) { for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { - std::string Part = (*I)->getValueAsString("Name"); + const StringRef Part = (*I)->getValueAsString("Name"); if ((*I)->getValueAsBit("Negated")) - SS << "!"; - SS << "LangOpts." + Part; + OS << "!"; + OS << "LangOpts." << Part; if (I + 1 != E) - SS << " || "; + OS << " || "; } } else - SS << "true"; - SS << "));\n"; + OS << "true"; + OS << "));\n"; } } - SS << "}\n\n"; - OS << SS.str(); + OS << "}\n\n"; return FnName; } @@ -1892,7 +1914,8 @@ void PragmaClangAttributeSupport::generateParsingHelpers(raw_ostream &OS) { continue; std::string SubRuleFunction; if (SubMatchRules.count(Rule.MetaSubject)) - SubRuleFunction = "isAttributeSubjectMatchSubRuleFor_" + Rule.getName(); + SubRuleFunction = + ("isAttributeSubjectMatchSubRuleFor_" + Rule.getName()).str(); else SubRuleFunction = "defaultIsAttributeSubjectMatchSubRuleFor"; OS << " Case(\"" << Rule.getName() << "\", std::make_pair(" @@ -2695,10 +2718,14 @@ static void GenerateHasAttrSpellingStringSwitch( // If this is the C++11 variety, also add in the LangOpts test. if (Variety == "CXX11") Test += " && LangOpts.CPlusPlus11"; + else if (Variety == "C2x") + Test += " && LangOpts.DoubleSquareBracketAttributes"; } else if (Variety == "CXX11") // C++11 mode should be checked against LangOpts, which is presumed to be // present in the caller. Test = "LangOpts.CPlusPlus11"; + else if (Variety == "C2x") + Test = "LangOpts.DoubleSquareBracketAttributes"; std::string TestStr = !Test.empty() ? Test + " ? " + llvm::itostr(Version) + " : 0" : "1"; @@ -2719,7 +2746,7 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) { // and declspecs. Then generate a big switch statement for each of them. std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); std::vector<Record *> Declspec, Microsoft, GNU, Pragma; - std::map<std::string, std::vector<Record *>> CXX; + std::map<std::string, std::vector<Record *>> CXX, C2x; // Walk over the list of all attributes, and split them out based on the // spelling variety. @@ -2735,6 +2762,8 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) { Microsoft.push_back(R); else if (Variety == "CXX11") CXX[SI.nameSpace()].push_back(R); + else if (Variety == "C2x") + C2x[SI.nameSpace()].push_back(R); else if (Variety == "Pragma") Pragma.push_back(R); } @@ -2754,20 +2783,25 @@ void EmitClangAttrHasAttrImpl(RecordKeeper &Records, raw_ostream &OS) { OS << "case AttrSyntax::Pragma:\n"; OS << " return llvm::StringSwitch<int>(Name)\n"; GenerateHasAttrSpellingStringSwitch(Pragma, OS, "Pragma"); - OS << "case AttrSyntax::CXX: {\n"; - // C++11-style attributes are further split out based on the Scope. - for (auto I = CXX.cbegin(), E = CXX.cend(); I != E; ++I) { - if (I != CXX.begin()) - OS << " else "; - if (I->first.empty()) - OS << "if (!Scope || Scope->getName() == \"\") {\n"; - else - OS << "if (Scope->getName() == \"" << I->first << "\") {\n"; - OS << " return llvm::StringSwitch<int>(Name)\n"; - GenerateHasAttrSpellingStringSwitch(I->second, OS, "CXX11", I->first); - OS << "}"; - } - OS << "\n}\n"; + auto fn = [&OS](const char *Spelling, const char *Variety, + const std::map<std::string, std::vector<Record *>> &List) { + OS << "case AttrSyntax::" << Variety << ": {\n"; + // C++11-style attributes are further split out based on the Scope. + for (auto I = List.cbegin(), E = List.cend(); I != E; ++I) { + if (I != List.cbegin()) + OS << " else "; + if (I->first.empty()) + OS << "if (!Scope || Scope->getName() == \"\") {\n"; + else + OS << "if (Scope->getName() == \"" << I->first << "\") {\n"; + OS << " return llvm::StringSwitch<int>(Name)\n"; + GenerateHasAttrSpellingStringSwitch(I->second, OS, Spelling, I->first); + OS << "}"; + } + OS << "\n} break;\n"; + }; + fn("CXX11", "CXX", CXX); + fn("C2x", "C", C2x); OS << "}\n"; } @@ -2788,10 +2822,11 @@ void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS) { << StringSwitch<unsigned>(Spellings[I].variety()) .Case("GNU", 0) .Case("CXX11", 1) - .Case("Declspec", 2) - .Case("Microsoft", 3) - .Case("Keyword", 4) - .Case("Pragma", 5) + .Case("C2x", 2) + .Case("Declspec", 3) + .Case("Microsoft", 4) + .Case("Keyword", 5) + .Case("Pragma", 6) .Default(0) << " && Scope == \"" << Spellings[I].nameSpace() << "\")\n" << " return " << I << ";\n"; @@ -2965,7 +3000,7 @@ static bool isArgVariadic(const Record &R, StringRef AttrName) { return createArgument(R, AttrName)->isVariadic(); } -static void emitArgInfo(const Record &R, std::stringstream &OS) { +static void emitArgInfo(const Record &R, raw_ostream &OS) { // This function will count the number of arguments specified for the // attribute and emit the number of required arguments followed by the // number of optional arguments. @@ -2994,136 +3029,72 @@ static void GenerateDefaultAppertainsTo(raw_ostream &OS) { OS << "}\n\n"; } +static std::string GetDiagnosticSpelling(const Record &R) { + std::string Ret = R.getValueAsString("DiagSpelling"); + if (!Ret.empty()) + return Ret; + + // If we couldn't find the DiagSpelling in this object, we can check to see + // if the object is one that has a base, and if it is, loop up to the Base + // member recursively. + std::string Super = R.getSuperClasses().back().first->getName(); + if (Super == "DDecl" || Super == "DStmt") + return GetDiagnosticSpelling(*R.getValueAsDef("Base")); + + return ""; +} + static std::string CalculateDiagnostic(const Record &S) { // If the SubjectList object has a custom diagnostic associated with it, // return that directly. - std::string CustomDiag = S.getValueAsString("CustomDiag"); + const StringRef CustomDiag = S.getValueAsString("CustomDiag"); if (!CustomDiag.empty()) - return CustomDiag; - - // Given the list of subjects, determine what diagnostic best fits. - enum { - Func = 1U << 0, - Var = 1U << 1, - ObjCMethod = 1U << 2, - Param = 1U << 3, - Class = 1U << 4, - GenericRecord = 1U << 5, - Type = 1U << 6, - ObjCIVar = 1U << 7, - ObjCProp = 1U << 8, - ObjCInterface = 1U << 9, - Block = 1U << 10, - Namespace = 1U << 11, - Field = 1U << 12, - CXXMethod = 1U << 13, - ObjCProtocol = 1U << 14, - Enum = 1U << 15, - Named = 1U << 16, - }; - uint32_t SubMask = 0; + return ("\"" + Twine(CustomDiag) + "\"").str(); + std::vector<std::string> DiagList; std::vector<Record *> Subjects = S.getValueAsListOfDefs("Subjects"); for (const auto *Subject : Subjects) { const Record &R = *Subject; - std::string Name; + // Get the diagnostic text from the Decl or Stmt node given. + std::string V = GetDiagnosticSpelling(R); + if (V.empty()) { + PrintError(R.getLoc(), + "Could not determine diagnostic spelling for the node: " + + R.getName() + "; please add one to DeclNodes.td"); + } else { + // The node may contain a list of elements itself, so split the elements + // by a comma, and trim any whitespace. + SmallVector<StringRef, 2> Frags; + llvm::SplitString(V, Frags, ","); + for (auto Str : Frags) { + DiagList.push_back(Str.trim()); + } + } + } - if (R.isSubClassOf("SubsetSubject")) { - PrintError(R.getLoc(), "SubsetSubjects should use a custom diagnostic"); - // As a fallback, look through the SubsetSubject to see what its base - // type is, and use that. This needs to be updated if SubsetSubjects - // are allowed within other SubsetSubjects. - Name = R.getValueAsDef("Base")->getName(); - } else - Name = R.getName(); - - uint32_t V = StringSwitch<uint32_t>(Name) - .Case("Function", Func) - .Case("Var", Var) - .Case("ObjCMethod", ObjCMethod) - .Case("ParmVar", Param) - .Case("TypedefName", Type) - .Case("ObjCIvar", ObjCIVar) - .Case("ObjCProperty", ObjCProp) - .Case("Record", GenericRecord) - .Case("ObjCInterface", ObjCInterface) - .Case("ObjCProtocol", ObjCProtocol) - .Case("Block", Block) - .Case("CXXRecord", Class) - .Case("Namespace", Namespace) - .Case("Field", Field) - .Case("CXXMethod", CXXMethod) - .Case("Enum", Enum) - .Case("Named", Named) - .Default(0); - if (!V) { - // Something wasn't in our mapping, so be helpful and let the developer - // know about it. - PrintFatalError(R.getLoc(), "Unknown subject type: " + R.getName()); - return ""; - } - - SubMask |= V; - } - - switch (SubMask) { - // For the simple cases where there's only a single entry in the mask, we - // don't have to resort to bit fiddling. - case Func: return "ExpectedFunction"; - case Var: return "ExpectedVariable"; - case Param: return "ExpectedParameter"; - case Class: return "ExpectedClass"; - case Enum: return "ExpectedEnum"; - case CXXMethod: - // FIXME: Currently, this maps to ExpectedMethod based on existing code, - // but should map to something a bit more accurate at some point. - case ObjCMethod: return "ExpectedMethod"; - case Type: return "ExpectedType"; - case ObjCInterface: return "ExpectedObjectiveCInterface"; - case ObjCProtocol: return "ExpectedObjectiveCProtocol"; - - // "GenericRecord" means struct, union or class; check the language options - // and if not compiling for C++, strip off the class part. Note that this - // relies on the fact that the context for this declares "Sema &S". - case GenericRecord: - return "(S.getLangOpts().CPlusPlus ? ExpectedStructOrUnionOrClass : " - "ExpectedStructOrUnion)"; - case Func | ObjCMethod | Block: return "ExpectedFunctionMethodOrBlock"; - case Func | ObjCMethod | Class: return "ExpectedFunctionMethodOrClass"; - case Func | Param: - case Func | ObjCMethod | Param: return "ExpectedFunctionMethodOrParameter"; - case Func | ObjCMethod: return "ExpectedFunctionOrMethod"; - case Func | Var: return "ExpectedVariableOrFunction"; - - // If not compiling for C++, the class portion does not apply. - case Func | Var | Class: - return "(S.getLangOpts().CPlusPlus ? ExpectedFunctionVariableOrClass : " - "ExpectedVariableOrFunction)"; - - case Func | Var | Class | ObjCInterface: - return "(S.getLangOpts().CPlusPlus" - " ? ((S.getLangOpts().ObjC1 || S.getLangOpts().ObjC2)" - " ? ExpectedFunctionVariableClassOrObjCInterface" - " : ExpectedFunctionVariableOrClass)" - " : ((S.getLangOpts().ObjC1 || S.getLangOpts().ObjC2)" - " ? ExpectedFunctionVariableOrObjCInterface" - " : ExpectedVariableOrFunction))"; - - case ObjCMethod | ObjCProp: return "ExpectedMethodOrProperty"; - case Func | ObjCMethod | ObjCProp: - return "ExpectedFunctionOrMethodOrProperty"; - case ObjCProtocol | ObjCInterface: - return "ExpectedObjectiveCInterfaceOrProtocol"; - case Field | Var: return "ExpectedFieldOrGlobalVar"; - - case Named: - return "ExpectedNamedDecl"; - } - - PrintFatalError(S.getLoc(), - "Could not deduce diagnostic argument for Attr subjects"); + if (DiagList.empty()) { + PrintFatalError(S.getLoc(), + "Could not deduce diagnostic argument for Attr subjects"); + return ""; + } - return ""; + // FIXME: this is not particularly good for localization purposes and ideally + // should be part of the diagnostics engine itself with some sort of list + // specifier. + + // A single member of the list can be returned directly. + if (DiagList.size() == 1) + return '"' + DiagList.front() + '"'; + + if (DiagList.size() == 2) + return '"' + DiagList[0] + " and " + DiagList[1] + '"'; + + // If there are more than two in the list, we serialize the first N - 1 + // elements with a comma. This leaves the string in the state: foo, bar, + // baz (but misses quux). We can then add ", and " for the last element + // manually. + std::string Diag = llvm::join(DiagList.begin(), DiagList.end() - 1, ", "); + return '"' + Diag + ", and " + *(DiagList.end() - 1) + '"'; } static std::string GetSubjectWithSuffix(const Record *R) { @@ -3210,8 +3181,8 @@ static std::string GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) { } SS << ") {\n"; SS << " S.Diag(Attr.getLoc(), diag::"; - SS << (Warn ? "warn_attribute_wrong_decl_type" : - "err_attribute_wrong_decl_type"); + SS << (Warn ? "warn_attribute_wrong_decl_type_str" : + "err_attribute_wrong_decl_type_str"); SS << ")\n"; SS << " << Attr.getName() << "; SS << CalculateDiagnostic(*SubjectObj) << ";\n"; @@ -3281,12 +3252,13 @@ static std::string GenerateLangOptRequirements(const Record &R, // codegen efficiency). std::string FnName = "check", Test; for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) { - std::string Part = (*I)->getValueAsString("Name"); + const StringRef Part = (*I)->getValueAsString("Name"); if ((*I)->getValueAsBit("Negated")) { FnName += "Not"; Test += "!"; } - Test += "S.LangOpts." + Part; + Test += "S.LangOpts."; + Test += Part; if (I + 1 != E) Test += " || "; FnName += Part; @@ -3342,7 +3314,7 @@ static std::string GenerateTargetRequirements(const Record &Attr, // applies to multiple target architectures. In order for the attribute to be // considered valid, all of its architectures need to be included. if (!Attr.isValueUnset("ParseKind")) { - std::string APK = Attr.getValueAsString("ParseKind"); + const StringRef APK = Attr.getValueAsString("ParseKind"); for (const auto &I : Dupes) { if (I.first == APK) { std::vector<StringRef> DA = @@ -3438,7 +3410,8 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) { // another mapping. At the same time, generate the AttrInfoMap object // contents. Due to the reliance on generated code, use separate streams so // that code will not be interleaved. - std::stringstream SS; + std::string Buffer; + raw_string_ostream SS {Buffer}; for (auto I = Attrs.begin(), E = Attrs.end(); I != E; ++I) { // TODO: If the attribute's kind appears in the list of duplicates, that is // because it is a target-specific attribute that appears multiple times. @@ -3484,7 +3457,7 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); std::vector<StringMatcher::StringPair> GNU, Declspec, Microsoft, CXX11, - Keywords, Pragma; + Keywords, Pragma, C2x; std::set<std::string> Seen; for (const auto *A : Attrs) { const Record &Attr = *A; @@ -3522,6 +3495,10 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { Matches = &CXX11; Spelling += S.nameSpace(); Spelling += "::"; + } else if (Variety == "C2x") { + Matches = &C2x; + Spelling += S.nameSpace(); + Spelling += "::"; } else if (Variety == "GNU") Matches = &GNU; else if (Variety == "Declspec") @@ -3560,6 +3537,8 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { StringMatcher("Name", Microsoft, OS).Emit(); OS << " } else if (AttributeList::AS_CXX11 == Syntax) {\n"; StringMatcher("Name", CXX11, OS).Emit(); + OS << " } else if (AttributeList::AS_C2x == Syntax) {\n"; + StringMatcher("Name", C2x, OS).Emit(); OS << " } else if (AttributeList::AS_Keyword == Syntax || "; OS << "AttributeList::AS_ContextSensitiveKeyword == Syntax) {\n"; StringMatcher("Name", Keywords, OS).Emit(); @@ -3624,20 +3603,25 @@ class DocumentationData { public: const Record *Documentation; const Record *Attribute; - - DocumentationData(const Record &Documentation, const Record &Attribute) - : Documentation(&Documentation), Attribute(&Attribute) {} + std::string Heading; + unsigned SupportedSpellings; + + DocumentationData(const Record &Documentation, const Record &Attribute, + const std::pair<std::string, unsigned> HeadingAndKinds) + : Documentation(&Documentation), Attribute(&Attribute), + Heading(std::move(HeadingAndKinds.first)), + SupportedSpellings(HeadingAndKinds.second) {} }; static void WriteCategoryHeader(const Record *DocCategory, raw_ostream &OS) { - const std::string &Name = DocCategory->getValueAsString("Name"); - OS << Name << "\n" << std::string(Name.length(), '=') << "\n"; + const StringRef Name = DocCategory->getValueAsString("Name"); + OS << Name << "\n" << std::string(Name.size(), '=') << "\n"; // If there is content, print that as well. - std::string ContentStr = DocCategory->getValueAsString("Content"); + const StringRef ContentStr = DocCategory->getValueAsString("Content"); // Trim leading and trailing newlines and spaces. - OS << StringRef(ContentStr).trim(); + OS << ContentStr.trim(); OS << "\n\n"; } @@ -3645,22 +3629,24 @@ static void WriteCategoryHeader(const Record *DocCategory, enum SpellingKind { GNU = 1 << 0, CXX11 = 1 << 1, - Declspec = 1 << 2, - Microsoft = 1 << 3, - Keyword = 1 << 4, - Pragma = 1 << 5 + C2x = 1 << 2, + Declspec = 1 << 3, + Microsoft = 1 << 4, + Keyword = 1 << 5, + Pragma = 1 << 6 }; -static void WriteDocumentation(RecordKeeper &Records, - const DocumentationData &Doc, raw_ostream &OS) { +static std::pair<std::string, unsigned> +GetAttributeHeadingAndSpellingKinds(const Record &Documentation, + const Record &Attribute) { // FIXME: there is no way to have a per-spelling category for the attribute // documentation. This may not be a limiting factor since the spellings // should generally be consistently applied across the category. - std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Doc.Attribute); + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attribute); // Determine the heading to be used for this attribute. - std::string Heading = Doc.Documentation->getValueAsString("Heading"); + std::string Heading = Documentation.getValueAsString("Heading"); bool CustomHeading = !Heading.empty(); if (Heading.empty()) { // If there's only one spelling, we can simply use that. @@ -3682,7 +3668,7 @@ static void WriteDocumentation(RecordKeeper &Records, // If the heading is still empty, it is an error. if (Heading.empty()) - PrintFatalError(Doc.Attribute->getLoc(), + PrintFatalError(Attribute.getLoc(), "This attribute requires a heading to be specified"); // Gather a list of unique spellings; this is not the same as the semantic @@ -3695,6 +3681,7 @@ static void WriteDocumentation(RecordKeeper &Records, SpellingKind Kind = StringSwitch<SpellingKind>(I.variety()) .Case("GNU", GNU) .Case("CXX11", CXX11) + .Case("C2x", C2x) .Case("Declspec", Declspec) .Case("Microsoft", Microsoft) .Case("Keyword", Keyword) @@ -3704,7 +3691,7 @@ static void WriteDocumentation(RecordKeeper &Records, SupportedSpellings |= Kind; std::string Name; - if (Kind == CXX11 && !I.nameSpace().empty()) + if ((Kind == CXX11 || Kind == C2x) && !I.nameSpace().empty()) Name = I.nameSpace() + "::"; Name += I.name(); @@ -3724,27 +3711,33 @@ static void WriteDocumentation(RecordKeeper &Records, } Heading += ")"; } - OS << Heading << "\n" << std::string(Heading.length(), '-') << "\n"; - if (!SupportedSpellings) - PrintFatalError(Doc.Attribute->getLoc(), + PrintFatalError(Attribute.getLoc(), "Attribute has no supported spellings; cannot be " "documented"); + return std::make_pair(std::move(Heading), SupportedSpellings); +} + +static void WriteDocumentation(RecordKeeper &Records, + const DocumentationData &Doc, raw_ostream &OS) { + OS << Doc.Heading << "\n" << std::string(Doc.Heading.length(), '-') << "\n"; // List what spelling syntaxes the attribute supports. OS << ".. csv-table:: Supported Syntaxes\n"; - OS << " :header: \"GNU\", \"C++11\", \"__declspec\", \"Keyword\","; + OS << " :header: \"GNU\", \"C++11\", \"C2x\", \"__declspec\", \"Keyword\","; OS << " \"Pragma\", \"Pragma clang attribute\"\n\n"; OS << " \""; - if (SupportedSpellings & GNU) OS << "X"; + if (Doc.SupportedSpellings & GNU) OS << "X"; + OS << "\",\""; + if (Doc.SupportedSpellings & CXX11) OS << "X"; OS << "\",\""; - if (SupportedSpellings & CXX11) OS << "X"; + if (Doc.SupportedSpellings & C2x) OS << "X"; OS << "\",\""; - if (SupportedSpellings & Declspec) OS << "X"; + if (Doc.SupportedSpellings & Declspec) OS << "X"; OS << "\",\""; - if (SupportedSpellings & Keyword) OS << "X"; + if (Doc.SupportedSpellings & Keyword) OS << "X"; OS << "\", \""; - if (SupportedSpellings & Pragma) OS << "X"; + if (Doc.SupportedSpellings & Pragma) OS << "X"; OS << "\", \""; if (getPragmaAttributeSupport(Records).isAttributedSupported(*Doc.Attribute)) OS << "X"; @@ -3756,16 +3749,16 @@ static void WriteDocumentation(RecordKeeper &Records, OS << "This attribute has been deprecated, and may be removed in a future " << "version of Clang."; const Record &Deprecated = *Doc.Documentation->getValueAsDef("Deprecated"); - std::string Replacement = Deprecated.getValueAsString("Replacement"); + const StringRef Replacement = Deprecated.getValueAsString("Replacement"); if (!Replacement.empty()) OS << " This attribute has been superseded by ``" << Replacement << "``."; OS << "\n\n"; } - std::string ContentStr = Doc.Documentation->getValueAsString("Content"); + const StringRef ContentStr = Doc.Documentation->getValueAsString("Content"); // Trim leading and trailing newlines and spaces. - OS << StringRef(ContentStr).trim(); + OS << ContentStr.trim(); OS << "\n\n\n"; } @@ -3794,23 +3787,29 @@ void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS) { // If the category is "undocumented", then there cannot be any other // documentation categories (otherwise, the attribute would become // documented). - std::string Cat = Category->getValueAsString("Name"); + const StringRef Cat = Category->getValueAsString("Name"); bool Undocumented = Cat == "Undocumented"; if (Undocumented && Docs.size() > 1) PrintFatalError(Doc.getLoc(), "Attribute is \"Undocumented\", but has multiple " - "documentation categories"); + "documentation categories"); if (!Undocumented) - SplitDocs[Category].push_back(DocumentationData(Doc, Attr)); + SplitDocs[Category].push_back(DocumentationData( + Doc, Attr, GetAttributeHeadingAndSpellingKinds(Doc, Attr))); } } // Having split the attributes out based on what documentation goes where, // we can begin to generate sections of documentation. - for (const auto &I : SplitDocs) { + for (auto &I : SplitDocs) { WriteCategoryHeader(I.first, OS); + std::sort(I.second.begin(), I.second.end(), + [](const DocumentationData &D1, const DocumentationData &D2) { + return D1.Heading < D2.Heading; + }); + // Walk over each of the attributes in the category and write out their // documentation. for (const auto &Doc : I.second) |