diff options
author | Matthias Gehre <M.Gehre@gmx.de> | 2019-07-25 17:50:51 +0000 |
---|---|---|
committer | Matthias Gehre <M.Gehre@gmx.de> | 2019-07-25 17:50:51 +0000 |
commit | d293cbd5fd44549bb48314499bc3b266d8967249 (patch) | |
tree | ab6670feffca7c33388bc45e9dba4f3aa4bef18b | |
parent | 393094859e45f7a6ccbade1c919dee2c2e2f3a7e (diff) | |
download | bcm5719-llvm-d293cbd5fd44549bb48314499bc3b266d8967249.tar.gz bcm5719-llvm-d293cbd5fd44549bb48314499bc3b266d8967249.zip |
Add lifetime categories attributes
Summary:
This is the first part of work announced in
"[RFC] Adding lifetime analysis to clang" [0],
i.e. the addition of the [[gsl::Owner(T)]] and
[[gsl::Pointer(T)]] attributes, which
will enable user-defined types to participate in
the lifetime analysis (which will be part of the
next PR).
The type `T` here is called "DerefType" in the paper,
and denotes the type that an Owner owns and a Pointer
points to. E.g. `std::vector<int>` should be annotated
with `[[gsl::Owner(int)]]` and
a `std::vector<int>::iterator` with `[[gsl::Pointer(int)]]`.
[0] http://lists.llvm.org/pipermail/cfe-dev/2018-November/060355.html
Reviewers: gribozavr
Subscribers: xazax.hun, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D63954
llvm-svn: 367040
-rw-r--r-- | clang/include/clang/Basic/Attr.td | 14 | ||||
-rw-r--r-- | clang/include/clang/Basic/AttrDocs.td | 68 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 5 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 27 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclAttr.cpp | 68 | ||||
-rw-r--r-- | clang/test/AST/ast-dump-attr.cpp | 17 | ||||
-rw-r--r-- | clang/test/Misc/pragma-attribute-supported-attributes-list.test | 2 | ||||
-rw-r--r-- | clang/test/SemaCXX/attr-gsl-owner-pointer.cpp | 107 | ||||
-rw-r--r-- | clang/test/SemaOpenCL/invalid-kernel-attrs.cl | 6 | ||||
-rw-r--r-- | clang/utils/TableGen/ClangAttrEmitter.cpp | 4 |
10 files changed, 306 insertions, 12 deletions
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index feebd56160e..9f9292d4e5f 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2796,6 +2796,20 @@ def TypeTagForDatatype : InheritableAttr { let Documentation = [TypeTagForDatatypeDocs]; } +def Owner : InheritableAttr { + let Spellings = [CXX11<"gsl", "Owner">]; + let Subjects = SubjectList<[Struct]>; + let Args = [TypeArgument<"DerefType", /*opt=*/1>]; + let Documentation = [LifetimeOwnerDocs]; +} + +def Pointer : InheritableAttr { + let Spellings = [CXX11<"gsl", "Pointer">]; + let Subjects = SubjectList<[Struct]>; + let Args = [TypeArgument<"DerefType", /*opt=*/1>]; + let Documentation = [LifetimePointerDocs]; +} + // Microsoft-related attributes def MSNoVTable : InheritableAttr, TargetSpecificAttr<TargetMicrosoftCXXABI> { diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 59825023180..81dec0fdef8 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4230,3 +4230,71 @@ not initialized on device side. It has internal linkage and is initialized by the initializer on host side. }]; } + +def LifetimeOwnerDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +.. Note:: This attribute is experimental and its effect on analysis is subject to change in + a future version of clang. + +The attribute ``[[gsl::Owner(T)]]`` applies to structs and classes that own an +object of type ``T``: + +.. code-block:: c++ + + class [[gsl::Owner(int)]] IntOwner { + private: + int value; + public: + int *getInt() { return &value; } + }; + +The argument ``T`` is optional and currently ignored. +This attribute may be used by analysis tools and has no effect on code +generation. + +See Pointer_ for an example. +}]; +} + +def LifetimePointerDocs : Documentation { + let Category = DocCatDecl; + let Content = [{ +.. Note:: This attribute is experimental and its effect on analysis is subject to change in + a future version of clang. + +The attribute ``[[gsl::Pointer(T)]]`` applies to structs and classes that behave +like pointers to an object of type ``T``: + +.. code-block:: c++ + + class [[gsl::Pointer(int)]] IntPointer { + private: + int *valuePointer; + public: + int *getInt() { return &valuePointer; } + }; + +The argument ``T`` is optional and currently ignored. +This attribute may be used by analysis tools and has no effect on code +generation. + +Example: +When constructing an instance of a class annotated like this (a Pointer) from +an instance of a class annotated with ``[[gsl::Owner]]`` (an Owner), +then the analysis will consider the Pointer to point inside the Owner. +When the Owner's lifetime ends, it will consider the Pointer to be dangling. + +.. code-block:: c++ + + int f() { + IntPointer P; + if (true) { + IntOwner O(7); + P = IntPointer(O); // P "points into" O + } // P is dangling + return P.get(); // error: Using a dangling Pointer. + } + +}]; +} diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 42facca4468..e58a0b16bd6 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2519,6 +2519,9 @@ def err_nsobject_attribute : Error< "'NSObject' attribute is for pointer types only">; def err_attributes_are_not_compatible : Error< "%0 and %1 attributes are not compatible">; +def err_attribute_invalid_argument : Error< + "%select{'void'|a reference type|an array type|a non-vector or " + "non-vectorizable scalar type}0 is an invalid argument to attribute %1">; def err_attribute_wrong_number_arguments : Error< "%0 attribute %plural{0:takes no arguments|1:takes one argument|" ":requires exactly %1 arguments}1">; @@ -2567,8 +2570,6 @@ def err_attribute_argument_out_of_range : Error< def err_init_priority_object_attr : Error< "can only use 'init_priority' attribute on file-scope definitions " "of objects of class type">; -def err_attribute_argument_vec_type_hint : Error< - "invalid attribute argument %0 - expecting a vector or vectorizable scalar type">; def err_attribute_argument_out_of_bounds : Error< "%0 attribute parameter %1 is out of bounds">; def err_attribute_only_once_per_parameter : Error< diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 73b4f50fda4..3cf1f82943c 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -335,6 +335,7 @@ unsigned Parser::ParseAttributeArgsCommon( ConsumeParen(); bool ChangeKWThisToIdent = attributeTreatsKeywordThisAsIdentifier(*AttrName); + bool AttributeIsTypeArgAttr = attributeIsTypeArgAttr(*AttrName); // Interpret "kw_this" as an identifier if the attributed requests it. if (ChangeKWThisToIdent && Tok.is(tok::kw_this)) @@ -360,6 +361,7 @@ unsigned Parser::ParseAttributeArgsCommon( ArgExprs.push_back(ParseIdentifierLoc()); } + ParsedType TheParsedType; if (!ArgExprs.empty() ? Tok.is(tok::comma) : Tok.isNot(tok::r_paren)) { // Eat the comma. if (!ArgExprs.empty()) @@ -372,8 +374,17 @@ unsigned Parser::ParseAttributeArgsCommon( Tok.setKind(tok::identifier); ExprResult ArgExpr; - if (Tok.is(tok::identifier) && - attributeHasVariadicIdentifierArg(*AttrName)) { + if (AttributeIsTypeArgAttr) { + TypeResult T = ParseTypeName(); + if (T.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return 0; + } + if (T.isUsable()) + TheParsedType = T.get(); + break; // FIXME: Multiple type arguments are not implemented. + } else if (Tok.is(tok::identifier) && + attributeHasVariadicIdentifierArg(*AttrName)) { ArgExprs.push_back(ParseIdentifierLoc()); } else { bool Uneval = attributeParsedArgsUnevaluated(*AttrName); @@ -397,14 +408,20 @@ unsigned Parser::ParseAttributeArgsCommon( SourceLocation RParen = Tok.getLocation(); if (!ExpectAndConsume(tok::r_paren)) { SourceLocation AttrLoc = ScopeLoc.isValid() ? ScopeLoc : AttrNameLoc; - Attrs.addNew(AttrName, SourceRange(AttrLoc, RParen), ScopeName, ScopeLoc, - ArgExprs.data(), ArgExprs.size(), Syntax); + + if (AttributeIsTypeArgAttr && !TheParsedType.get().isNull()) { + Attrs.addNewTypeAttr(AttrName, SourceRange(AttrNameLoc, RParen), + ScopeName, ScopeLoc, TheParsedType, Syntax); + } else { + Attrs.addNew(AttrName, SourceRange(AttrLoc, RParen), ScopeName, ScopeLoc, + ArgExprs.data(), ArgExprs.size(), Syntax); + } } if (EndLoc) *EndLoc = RParen; - return static_cast<unsigned>(ArgExprs.size()); + return static_cast<unsigned>(ArgExprs.size() + !TheParsedType.get().isNull()); } /// Parse the arguments to a parameterized GNU attribute or diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 25632e63e7b..950dcac1bbc 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2954,8 +2954,7 @@ static void handleVecTypeHint(Sema &S, Decl *D, const ParsedAttr &AL) { if (!ParmType->isExtVectorType() && !ParmType->isFloatingType() && (ParmType->isBooleanType() || !ParmType->isIntegralType(S.getASTContext()))) { - S.Diag(AL.getLoc(), diag::err_attribute_argument_vec_type_hint) - << ParmType; + S.Diag(AL.getLoc(), diag::err_attribute_invalid_argument) << 3 << AL; return; } @@ -4555,6 +4554,67 @@ static void handleSuppressAttr(Sema &S, Decl *D, const ParsedAttr &AL) { DiagnosticIdentifiers.size(), AL.getAttributeSpellingListIndex())); } +static void handleLifetimeCategoryAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + TypeSourceInfo *DerefTypeLoc = nullptr; + QualType ParmType; + if (AL.hasParsedType()) { + ParmType = S.GetTypeFromParser(AL.getTypeArg(), &DerefTypeLoc); + + unsigned SelectIdx = ~0U; + if (ParmType->isVoidType()) + SelectIdx = 0; + else if (ParmType->isReferenceType()) + SelectIdx = 1; + else if (ParmType->isArrayType()) + SelectIdx = 2; + + if (SelectIdx != ~0U) { + S.Diag(AL.getLoc(), diag::err_attribute_invalid_argument) + << SelectIdx << AL; + return; + } + } + + // To check if earlier decl attributes do not conflict the newly parsed ones + // we always add (and check) the attribute to the cannonical decl. + D = D->getCanonicalDecl(); + if (AL.getKind() == ParsedAttr::AT_Owner) { + if (checkAttrMutualExclusion<PointerAttr>(S, D, AL)) + return; + if (const auto *OAttr = D->getAttr<OwnerAttr>()) { + const Type *ExistingDerefType = OAttr->getDerefTypeLoc() + ? OAttr->getDerefType().getTypePtr() + : nullptr; + if (ExistingDerefType != ParmType.getTypePtrOrNull()) { + S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) + << AL << OAttr; + S.Diag(OAttr->getLocation(), diag::note_conflicting_attribute); + } + return; + } + D->addAttr(::new (S.Context) + OwnerAttr(AL.getRange(), S.Context, DerefTypeLoc, + AL.getAttributeSpellingListIndex())); + } else { + if (checkAttrMutualExclusion<OwnerAttr>(S, D, AL)) + return; + if (const auto *PAttr = D->getAttr<PointerAttr>()) { + const Type *ExistingDerefType = PAttr->getDerefTypeLoc() + ? PAttr->getDerefType().getTypePtr() + : nullptr; + if (ExistingDerefType != ParmType.getTypePtrOrNull()) { + S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible) + << AL << PAttr; + S.Diag(PAttr->getLocation(), diag::note_conflicting_attribute); + } + return; + } + D->addAttr(::new (S.Context) + PointerAttr(AL.getRange(), S.Context, DerefTypeLoc, + AL.getAttributeSpellingListIndex())); + } +} + bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC, const FunctionDecl *FD) { if (Attrs.isInvalid()) @@ -7158,6 +7218,10 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_Suppress: handleSuppressAttr(S, D, AL); break; + case ParsedAttr::AT_Owner: + case ParsedAttr::AT_Pointer: + handleLifetimeCategoryAttr(S, D, AL); + break; case ParsedAttr::AT_OpenCLKernel: handleSimpleAttribute<OpenCLKernelAttr>(S, D, AL); break; diff --git a/clang/test/AST/ast-dump-attr.cpp b/clang/test/AST/ast-dump-attr.cpp index 8f67b9934e1..83c4a6342db 100644 --- a/clang/test/AST/ast-dump-attr.cpp +++ b/clang/test/AST/ast-dump-attr.cpp @@ -211,6 +211,23 @@ namespace TestSuppress { }
}
+namespace TestLifetimeCategories {
+class [[gsl::Owner(int)]] AOwner{};
+// CHECK: CXXRecordDecl{{.*}} class AOwner
+// CHECK: OwnerAttr {{.*}} int
+class [[gsl::Pointer(int)]] APointer{};
+// CHECK: CXXRecordDecl{{.*}} class APointer
+// CHECK: PointerAttr {{.*}} int
+
+class [[gsl::Pointer]] PointerWithoutArgument{};
+// CHECK: CXXRecordDecl{{.*}} class PointerWithoutArgument
+// CHECK: PointerAttr
+
+class [[gsl::Owner]] OwnerWithoutArgument{};
+// CHECK: CXXRecordDecl{{.*}} class OwnerWithoutArgument
+// CHECK: OwnerAttr
+} // namespace TestLifetimeCategories
+
// Verify the order of attributes in the Ast. It must reflect the order
// in the parsed source.
int mergeAttrTest() __attribute__((deprecated)) __attribute__((warn_unused_result));
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index fc9d86efe70..cc2d3806018 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -117,8 +117,10 @@ // CHECK-NEXT: OpenCLNoSVM (SubjectMatchRule_variable) // CHECK-NEXT: OptimizeNone (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: Overloadable (SubjectMatchRule_function) +// CHECK-NEXT: Owner (SubjectMatchRule_record_not_is_union) // CHECK-NEXT: ParamTypestate (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union) // CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function) // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: RequireConstantInit (SubjectMatchRule_variable_is_global) diff --git a/clang/test/SemaCXX/attr-gsl-owner-pointer.cpp b/clang/test/SemaCXX/attr-gsl-owner-pointer.cpp new file mode 100644 index 00000000000..1c3deb3982e --- /dev/null +++ b/clang/test/SemaCXX/attr-gsl-owner-pointer.cpp @@ -0,0 +1,107 @@ +// RUN: %clang_cc1 -verify -ast-dump %s | \ +// RUN: FileCheck --implicit-check-not OwnerAttr --implicit-check-not PointerAttr %s + +int [[gsl::Owner]] i; +// expected-error@-1 {{'Owner' attribute cannot be applied to types}} +void [[gsl::Owner]] f(); +// expected-error@-1 {{'Owner' attribute cannot be applied to types}} + +[[gsl::Owner]] void f(); +// expected-warning@-1 {{'Owner' attribute only applies to structs}} + +union [[gsl::Owner(int)]] Union{}; +// expected-warning@-1 {{'Owner' attribute only applies to structs}} + +struct S { +}; + +S [[gsl::Owner]] Instance; +// expected-error@-1 {{'Owner' attribute cannot be applied to types}} + +class [[gsl::Owner(7)]] OwnerDerefNoType{}; +// expected-error@-1 {{expected a type}} + +class [[gsl::Pointer("int")]] PointerDerefNoType{}; +// expected-error@-1 {{expected a type}} + +class [[gsl::Owner(int)]] [[gsl::Pointer(int)]] BothOwnerPointer{}; +// expected-error@-1 {{'Pointer' and 'Owner' attributes are not compatible}} +// expected-note@-2 {{conflicting attribute is here}} +// CHECK: CXXRecordDecl {{.*}} BothOwnerPointer +// CHECK: OwnerAttr {{.*}} int + +class [[gsl::Owner(void)]] OwnerVoidDerefType{}; +// expected-error@-1 {{'void' is an invalid argument to attribute 'Owner'}} +class [[gsl::Pointer(void)]] PointerVoidDerefType{}; +// expected-error@-1 {{'void' is an invalid argument to attribute 'Pointer'}} + +class [[gsl::Pointer(int)]] AddConflictLater{}; +// CHECK: CXXRecordDecl {{.*}} AddConflictLater +// CHECK: PointerAttr {{.*}} int +class [[gsl::Owner(int)]] AddConflictLater; +// expected-error@-1 {{'Owner' and 'Pointer' attributes are not compatible}} +// expected-note@-5 {{conflicting attribute is here}} +// CHECK: CXXRecordDecl {{.*}} AddConflictLater +// CHECK: PointerAttr {{.*}} Inherited int + +class [[gsl::Owner(int)]] AddConflictLater2{}; +// CHECK: CXXRecordDecl {{.*}} AddConflictLater2 +// CHECK: OwnerAttr {{.*}} int +class [[gsl::Owner(float)]] AddConflictLater2; +// expected-error@-1 {{'Owner' and 'Owner' attributes are not compatible}} +// expected-note@-5 {{conflicting attribute is here}} +// CHECK: CXXRecordDecl {{.*}} AddConflictLater +// CHECK: OwnerAttr {{.*}} Inherited int + +class [[gsl::Owner()]] [[gsl::Owner(int)]] WithAndWithoutParameter{}; +// expected-error@-1 {{'Owner' and 'Owner' attributes are not compatible}} +// expected-note@-2 {{conflicting attribute is here}} +// CHECK: CXXRecordDecl {{.*}} WithAndWithoutParameter +// CHECK: OwnerAttr + +class [[gsl::Owner(int &)]] ReferenceType{}; +// expected-error@-1 {{a reference type is an invalid argument to attribute 'Owner'}} + +class [[gsl::Pointer(int[])]] ArrayType{}; +// expected-error@-1 {{an array type is an invalid argument to attribute 'Pointer'}} + +class [[gsl::Owner]] OwnerMissingParameter{}; +// CHECK: CXXRecordDecl {{.*}} OwnerMissingParameter +// CHECK: OwnerAttr + +class [[gsl::Pointer]] PointerMissingParameter{}; +// CHECK: CXXRecordDecl {{.*}} PointerMissingParameter +// CHECK: PointerAttr + +class [[gsl::Owner()]] OwnerWithEmptyParameterList{}; +// CHECK: CXXRecordDecl {{.*}} OwnerWithEmptyParameterList +// CHECK: OwnerAttr {{.*}} + +class [[gsl::Pointer()]] PointerWithEmptyParameterList{}; +// CHECK: CXXRecordDecl {{.*}} PointerWithEmptyParameterList +// CHECK: PointerAttr {{.*}} + +struct [[gsl::Owner(int)]] AnOwner{}; +// CHECK: CXXRecordDecl {{.*}} AnOwner +// CHECK: OwnerAttr {{.*}} int + +struct S; +class [[gsl::Pointer(S)]] APointer{}; +// CHECK: CXXRecordDecl {{.*}} APointer +// CHECK: PointerAttr {{.*}} S + +class [[gsl::Owner(int)]] [[gsl::Owner(int)]] DuplicateOwner{}; +// CHECK: CXXRecordDecl {{.*}} DuplicateOwner +// CHECK: OwnerAttr {{.*}} int + +class [[gsl::Pointer(int)]] [[gsl::Pointer(int)]] DuplicatePointer{}; +// CHECK: CXXRecordDecl {{.*}} DuplicatePointer +// CHECK: PointerAttr {{.*}} int + +class [[gsl::Owner(int)]] AddTheSameLater{}; +// CHECK: CXXRecordDecl {{.*}} AddTheSameLater +// CHECK: OwnerAttr {{.*}} int + +class [[gsl::Owner(int)]] AddTheSameLater; +// CHECK: CXXRecordDecl {{.*}} prev {{.*}} AddTheSameLater +// CHECK: OwnerAttr {{.*}} int diff --git a/clang/test/SemaOpenCL/invalid-kernel-attrs.cl b/clang/test/SemaOpenCL/invalid-kernel-attrs.cl index a96e85c9ee2..daa8fa07f68 100644 --- a/clang/test/SemaOpenCL/invalid-kernel-attrs.cl +++ b/clang/test/SemaOpenCL/invalid-kernel-attrs.cl @@ -1,12 +1,12 @@ -// RUN: %clang_cc1 -verify %s +// RUN: %clang_cc1 -verify %s kernel __attribute__((vec_type_hint)) void kernel1() {} //expected-error{{'vec_type_hint' attribute takes one argument}} kernel __attribute__((vec_type_hint(not_type))) void kernel2() {} //expected-error{{unknown type name 'not_type'}} -kernel __attribute__((vec_type_hint(void))) void kernel3() {} //expected-error{{invalid attribute argument 'void' - expecting a vector or vectorizable scalar type}} +kernel __attribute__((vec_type_hint(void))) void kernel3() {} //expected-error{{a non-vector or non-vectorizable scalar type is an invalid argument to attribute 'vec_type_hint'}} -kernel __attribute__((vec_type_hint(bool))) void kernel4() {} //expected-error{{invalid attribute argument 'bool' - expecting a vector or vectorizable scalar type}} +kernel __attribute__((vec_type_hint(bool))) void kernel4() {} //expected-error{{a non-vector or non-vectorizable scalar type is an invalid argument to attribute 'vec_type_hint'}} kernel __attribute__((vec_type_hint(int))) __attribute__((vec_type_hint(float))) void kernel5() {} //expected-warning{{attribute 'vec_type_hint' is already applied with different parameters}} diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index f315262ad0f..0489bb073d2 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -303,6 +303,8 @@ namespace { std::string getIsOmitted() const override { if (type == "IdentifierInfo *") return "!get" + getUpperName().str() + "()"; + if (type == "TypeSourceInfo *") + return "!get" + getUpperName().str() + "Loc()"; if (type == "ParamIdx") return "!get" + getUpperName().str() + "().isValid()"; return "false"; @@ -336,6 +338,8 @@ namespace { << " OS << \" \" << SA->get" << getUpperName() << "()->getName();\n"; } else if (type == "TypeSourceInfo *") { + if (isOptional()) + OS << " if (SA->get" << getUpperName() << "Loc())"; OS << " OS << \" \" << SA->get" << getUpperName() << "().getAsString();\n"; } else if (type == "bool") { |