From e2ceaea5f5ea5786a638e29e92d7d48d7188e7c6 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Wed, 19 Feb 2014 22:59:32 +0000 Subject: [PATCH] Refactored the way attribute category headers are handled so that it is possible to use custom categories. This allows for moving the consumable attributes (consumable, callable_when, return_typestate, etc) to be grouped together, with a content heading, like they were in the language extensions documentation. Moved the consumable attribute documentation from the language extensions into the attribute documentation table. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@201732 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LanguageExtensions.rst | 58 ---------------------- include/clang/Basic/Attr.td | 21 ++++---- include/clang/Basic/AttrDocs.td | 73 ++++++++++++++++++++++++++++ utils/TableGen/ClangAttrEmitter.cpp | 74 ++++++++++------------------- 4 files changed, 109 insertions(+), 117 deletions(-) diff --git a/docs/LanguageExtensions.rst b/docs/LanguageExtensions.rst index 457b0a386b..be80a7640f 100644 --- a/docs/LanguageExtensions.rst +++ b/docs/LanguageExtensions.rst @@ -1697,64 +1697,6 @@ The complete list of thread safety attributes, along with examples and frequently asked questions, can be found in the main documentation: see :doc:`ThreadSafetyAnalysis`. - -Consumed Annotation Checking -============================ - -Clang supports additional attributes for checking basic resource management -properties, specifically for unique objects that have a single owning reference. -The following attributes are currently supported, although **the implementation -for these annotations is currently in development and are subject to change.** - -``consumable`` --------------- - -Each class that uses any of the following annotations must first be marked -using the consumable attribute. Failure to do so will result in a warning. - -``set_typestate(new_state)`` ----------------------------- - -Annotate methods that transition an object into a new state with -``__attribute__((set_typestate(new_state)))``. The new new state must be -unconsumed, consumed, or unknown. - -``callable_when(...)`` ----------------------- - -Use ``__attribute__((callable_when(...)))`` to indicate what states a method -may be called in. Valid states are unconsumed, consumed, or unknown. Each -argument to this attribute must be a quoted string. E.g.: - -``__attribute__((callable_when("unconsumed", "unknown")))`` - -``tests_typestate(tested_state)`` ---------------------------------- - -Use ``__attribute__((tests_typestate(tested_state)))`` to indicate that a method -returns true if the object is in the specified state.. - -``param_typestate(expected_state)`` ------------------------------------ - -This attribute specifies expectations about function parameters. Calls to an -function with annotated parameters will issue a warning if the corresponding -argument isn't in the expected state. The attribute is also used to set the -initial state of the parameter when analyzing the function's body. - -``return_typestate(ret_state)`` -------------------------------- - -The ``return_typestate`` attribute can be applied to functions or parameters. -When applied to a function the attribute specifies the state of the returned -value. The function's body is checked to ensure that it always returns a value -in the specified state. On the caller side, values returned by the annotated -function are initialized to the given state. - -If the attribute is applied to a function parameter it modifies the state of -an argument after a call to the function returns. The function's body is -checked to ensure that the parameter is in the expected state before returning. - Type Safety Checking ==================== diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 6570214625..922dfa53fe 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -11,11 +11,12 @@ // specific documentation that is collated within the larger document. class DocumentationCategory { string Name = name; + code Content = [{}]; } -def DocCatFunction : DocumentationCategory<"Functions">; -def DocCatVariable : DocumentationCategory<"Variables">; -def DocCatType : DocumentationCategory<"Types">; -def DocCatStmt : DocumentationCategory<"Statements">; +def DocCatFunction : DocumentationCategory<"Function Attributes">; +def DocCatVariable : DocumentationCategory<"Variable Attributes">; +def DocCatType : DocumentationCategory<"Type Attributes">; +def DocCatStmt : DocumentationCategory<"Statement Attributes">; // Attributes listed under the Undocumented category do not generate any public // documentation. Ideally, this category should be used for internal-only // attributes which contain no spellings. @@ -1484,7 +1485,7 @@ def Consumable : InheritableAttr { let Args = [EnumArgument<"DefaultState", "ConsumedState", ["unknown", "consumed", "unconsumed"], ["Unknown", "Consumed", "Unconsumed"]>]; - let Documentation = [Undocumented]; + let Documentation = [ConsumableDocs]; } def ConsumableAutoCast : InheritableAttr { @@ -1505,7 +1506,7 @@ def CallableWhen : InheritableAttr { let Args = [VariadicEnumArgument<"CallableState", "ConsumedState", ["unknown", "consumed", "unconsumed"], ["Unknown", "Consumed", "Unconsumed"]>]; - let Documentation = [Undocumented]; + let Documentation = [CallableWhenDocs]; } def ParamTypestate : InheritableAttr { @@ -1514,7 +1515,7 @@ def ParamTypestate : InheritableAttr { let Args = [EnumArgument<"ParamState", "ConsumedState", ["unknown", "consumed", "unconsumed"], ["Unknown", "Consumed", "Unconsumed"]>]; - let Documentation = [Undocumented]; + let Documentation = [ParamTypestateDocs]; } def ReturnTypestate : InheritableAttr { @@ -1523,7 +1524,7 @@ def ReturnTypestate : InheritableAttr { let Args = [EnumArgument<"State", "ConsumedState", ["unknown", "consumed", "unconsumed"], ["Unknown", "Consumed", "Unconsumed"]>]; - let Documentation = [Undocumented]; + let Documentation = [ReturnTypestateDocs]; } def SetTypestate : InheritableAttr { @@ -1532,7 +1533,7 @@ def SetTypestate : InheritableAttr { let Args = [EnumArgument<"NewState", "ConsumedState", ["unknown", "consumed", "unconsumed"], ["Unknown", "Consumed", "Unconsumed"]>]; - let Documentation = [Undocumented]; + let Documentation = [SetTypestateDocs]; } def TestTypestate : InheritableAttr { @@ -1541,7 +1542,7 @@ def TestTypestate : InheritableAttr { let Args = [EnumArgument<"TestState", "ConsumedState", ["consumed", "unconsumed"], ["Consumed", "Unconsumed"]>]; - let Documentation = [Undocumented]; + let Documentation = [TestTypestateDocs]; } // Type safety attributes for `void *' pointers and type tags. diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 69cba427c1..e344946d9d 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -498,3 +498,76 @@ The semantics are as follows: a sequence equivalent to "movs pc, lr" will be used. }]; } + +def DocCatConsumed : DocumentationCategory<"Consumed Annotation Checking"> { + let Content = [{ +Clang supports additional attributes for checking basic resource management +properties, specifically for unique objects that have a single owning reference. +The following attributes are currently supported, although **the implementation +for these annotations is currently in development and are subject to change.** + }]; +} + +def SetTypestateDocs : Documentation { + let Category = DocCatConsumed; + let Content = [{ +Annotate methods that transition an object into a new state with +``__attribute__((set_typestate(new_state)))``. The new new state must be +unconsumed, consumed, or unknown. + }]; +} + +def CallableWhenDocs : Documentation { + let Category = DocCatConsumed; + let Content = [{ +Use ``__attribute__((callable_when(...)))`` to indicate what states a method +may be called in. Valid states are unconsumed, consumed, or unknown. Each +argument to this attribute must be a quoted string. E.g.: + +``__attribute__((callable_when("unconsumed", "unknown")))`` + }]; +} + +def TestTypestateDocs : Documentation { + let Category = DocCatConsumed; + let Content = [{ +Use ``__attribute__((test_typestate(tested_state)))`` to indicate that a method +returns true if the object is in the specified state.. + }]; +} + +def ParamTypestateDocs : Documentation { + let Category = DocCatConsumed; + let Content = [{ +This attribute specifies expectations about function parameters. Calls to an +function with annotated parameters will issue a warning if the corresponding +argument isn't in the expected state. The attribute is also used to set the +initial state of the parameter when analyzing the function's body. + }]; +} + +def ReturnTypestateDocs : Documentation { + let Category = DocCatConsumed; + let Content = [{ +The ``return_typestate`` attribute can be applied to functions or parameters. +When applied to a function the attribute specifies the state of the returned +value. The function's body is checked to ensure that it always returns a value +in the specified state. On the caller side, values returned by the annotated +function are initialized to the given state. + +When applied to a function parameter it modifies the state of an argument after +a call to the function returns. The function's body is checked to ensure that +the parameter is in the expected state before returning. + }]; +} + +def ConsumableDocs : Documentation { + let Category = DocCatConsumed; + let Content = [{ +Each ``class`` that uses any of the typestate annotations must first be marked +using the ``consumable`` attribute. Failure to do so will result in a warning. + +This attribute accepts a single parameter that must be one of the following: +``unknown``, ``consumed``, or ``unconsumed``. + }]; +} diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 4c94e208a6..8f65d91085 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -2648,49 +2648,32 @@ void EmitClangAttrParserStringSwitches(RecordKeeper &Records, class DocumentationData { public: - enum DocCategory { - Function, - Variable, - Type, - Statement, - Undocumented - }; - - DocCategory Category; const Record *Documentation; const Record *Attribute; - DocumentationData(DocCategory Category, const Record &Documentation, - const Record &Attribute) - : Category(Category), Documentation(&Documentation), - Attribute(&Attribute) {} + DocumentationData(const Record &Documentation, const Record &Attribute) + : Documentation(&Documentation), Attribute(&Attribute) {} }; -static void WriteCategoryHeader(DocumentationData::DocCategory Category, +static void WriteCategoryHeader(const Record *DocCategory, raw_ostream &OS) { - OS << "\n"; - switch (Category) { - case DocumentationData::Undocumented: - assert(false && "Undocumented attributes are not documented!"); - break; - case DocumentationData::Function: - OS << "Function Attributes\n"; - OS << "===================\n"; - break; - case DocumentationData::Variable: - OS << "Variable Attributes\n"; - OS << "===================\n"; - break; - case DocumentationData::Type: - OS << "Type Attributes\n"; - OS << "===============\n"; - break; - case DocumentationData::Statement: - OS << "Statement Attributes\n"; - OS << "====================\n"; - break; + const std::string &Name = DocCategory->getValueAsString("Name"); + OS << Name << "\n" << std::string(Name.length(), '=') << "\n"; + + // If there is content, print that as well. + std::string ContentStr = DocCategory->getValueAsString("Content"); + if (!ContentStr.empty()) { + // Trim leading and trailing newlines and spaces. + StringRef Content(ContentStr); + while (Content.startswith("\r") || Content.startswith("\n") || + Content.startswith(" ") || Content.startswith("\t")) + Content = Content.substr(1); + while (Content.endswith("\r") || Content.endswith("\n") || + Content.endswith(" ") || Content.endswith("\t")) + Content = Content.substr(0, Content.size() - 1); + OS << Content; } - OS << "\n"; + OS << "\n\n"; } enum SpellingKind { @@ -2828,9 +2811,9 @@ void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS) { return; } - OS << Documentation->getValueAsString("Intro"); + OS << Documentation->getValueAsString("Intro") << "\n"; - typedef std::map > CategoryMap; CategoryMap SplitDocs; @@ -2844,26 +2827,19 @@ void EmitClangAttrDocs(RecordKeeper &Records, raw_ostream &OS) { for (std::vector::const_iterator DI = Docs.begin(), DE = Docs.end(); DI != DE; ++DI) { const Record &Doc = **DI; - DocumentationData::DocCategory Cat = - StringSwitch( - Doc.getValueAsDef("Category")->getValueAsString("Name")) - .Case("Functions", DocumentationData::Function) - .Case("Variables", DocumentationData::Variable) - .Case("Types", DocumentationData::Type) - .Case("Statements", DocumentationData::Statement) - .Case("Undocumented", DocumentationData::Undocumented); - + const Record *Category = Doc.getValueAsDef("Category"); // If the category is "undocumented", then there cannot be any other // documentation categories (otherwise, the attribute would become // documented). - bool Undocumented = DocumentationData::Undocumented == Cat; + std::string Cat = Category->getValueAsString("Name"); + bool Undocumented = Cat == "Undocumented"; if (Undocumented && Docs.size() > 1) PrintFatalError(Doc.getLoc(), "Attribute is \"Undocumented\", but has multiple " "documentation categories"); if (!Undocumented) - SplitDocs[Cat].push_back(DocumentationData(Cat, Doc, Attr)); + SplitDocs[Category].push_back(DocumentationData(Doc, Attr)); } } -- 2.40.0