Check for consistent use of nullability type specifiers in a header.

Adds a new warning (under -Wnullability-completeness) that complains
about pointer, block pointer, or member pointer declarations that have
not been annotated with nullability information (directly or inferred)
within a header that contains some nullability annotations. This is
intended to be used to help maintain the completeness of nullability
information within a header that has already been audited.

Note that, for performance reasons, this warning will underrepresent
the number of non-annotated pointers in the case where more than one
pointer is seen before the first nullability type specifier, because
we're only tracking one piece of information per header. Part of
rdar://problem/18868820.

llvm-svn: 240158
This commit is contained in:
Douglas Gregor 2015-06-19 18:27:45 +00:00
parent a59a7211f0
commit bec595a641
17 changed files with 334 additions and 53 deletions

View File

@ -251,6 +251,7 @@ def NewlineEOF : DiagGroup<"newline-eof">;
def Nullability : DiagGroup<"nullability">;
def NullabilityDeclSpec : DiagGroup<"nullability-declspec">;
def NullableToNonNullConversion : DiagGroup<"nullable-to-nonnull-conversion">;
def NullabilityCompleteness : DiagGroup<"nullability-completeness">;
def NullArithmetic : DiagGroup<"null-arithmetic">;
def NullCharacter : DiagGroup<"null-character">;
def NullDereference : DiagGroup<"null-dereference">;

View File

@ -7718,6 +7718,11 @@ def warn_null_resettable_setter : Warning<
"synthesized setter %0 for null_resettable property %1 does not handle nil">,
InGroup<Nullability>;
def warn_nullability_missing : Warning<
"%select{pointer|block pointer|member pointer}0 is missing a nullability "
"type specifier (__nonnull, __nullable, or __null_unspecified)">,
InGroup<NullabilityCompleteness>;
}
} // end of sema component.

View File

@ -81,6 +81,8 @@ public:
AS_Declspec,
/// __ptr16, alignas(...), etc.
AS_Keyword,
/// Context-sensitive version of a keyword attribute.
AS_ContextSensitiveKeyword,
/// #pragma ...
AS_Pragma
};
@ -120,9 +122,6 @@ private:
/// True if this has a ParsedType
unsigned HasParsedType : 1;
/// True when this keyword attribute is a context-sensitive keyword.
unsigned IsContextSensitiveKeyword : 1;
unsigned AttrKind : 8;
/// \brief The location of the 'unavailable' keyword in an
@ -223,8 +222,7 @@ private:
ScopeLoc(scopeLoc), EllipsisLoc(ellipsisLoc), NumArgs(numArgs),
SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false),
IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false),
HasParsedType(false), IsContextSensitiveKeyword(false),
NextInPosition(nullptr), NextInPool(nullptr) {
HasParsedType(false), NextInPosition(nullptr), NextInPool(nullptr) {
if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion));
AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
}
@ -242,8 +240,8 @@ private:
ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed),
Invalid(false), UsedAsTypeAttr(false), IsAvailability(true),
IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
IsContextSensitiveKeyword(false), UnavailableLoc(unavailable),
MessageExpr(messageExpr), NextInPosition(nullptr), NextInPool(nullptr) {
UnavailableLoc(unavailable), MessageExpr(messageExpr),
NextInPosition(nullptr), NextInPool(nullptr) {
ArgsUnion PVal(Parm);
memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion));
new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced);
@ -263,8 +261,7 @@ private:
ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(3), SyntaxUsed(syntaxUsed),
Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
IsContextSensitiveKeyword(false), NextInPosition(nullptr),
NextInPool(nullptr) {
NextInPosition(nullptr), NextInPool(nullptr) {
ArgsVector Args;
Args.push_back(Parm1);
Args.push_back(Parm2);
@ -282,8 +279,7 @@ private:
ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed),
Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false),
IsContextSensitiveKeyword(false), NextInPosition(nullptr),
NextInPool(nullptr) {
NextInPosition(nullptr), NextInPool(nullptr) {
ArgsUnion PVal(ArgKind);
memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion));
TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot();
@ -301,8 +297,7 @@ private:
ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed),
Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true),
IsContextSensitiveKeyword(false), NextInPosition(nullptr),
NextInPool(nullptr) {
NextInPosition(nullptr), NextInPool(nullptr) {
new (&getTypeBuffer()) ParsedType(typeArg);
AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
}
@ -316,8 +311,7 @@ private:
ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed),
Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false),
IsContextSensitiveKeyword(false), NextInPosition(nullptr),
NextInPool(nullptr) {
NextInPosition(nullptr), NextInPool(nullptr) {
new (&getPropertyDataBuffer()) PropertyData(getterId, setterId);
AttrKind = getKind(getName(), getScopeName(), syntaxUsed);
}
@ -351,22 +345,19 @@ public:
bool isAlignasAttribute() const {
// FIXME: Use a better mechanism to determine this.
return getKind() == AT_Aligned && SyntaxUsed == AS_Keyword;
return getKind() == AT_Aligned && isKeywordAttribute();
}
bool isDeclspecAttribute() const { return SyntaxUsed == AS_Declspec; }
bool isCXX11Attribute() const {
return SyntaxUsed == AS_CXX11 || isAlignasAttribute();
}
bool isKeywordAttribute() const { return SyntaxUsed == AS_Keyword; }
bool isContextSensitiveKeywordAttribute() const {
return IsContextSensitiveKeyword;
bool isKeywordAttribute() const {
return SyntaxUsed == AS_Keyword || SyntaxUsed == AS_ContextSensitiveKeyword;
}
void setContextSensitiveKeywordAttribute() {
assert(SyntaxUsed == AS_Keyword);
IsContextSensitiveKeyword = true;
bool isContextSensitiveKeywordAttribute() const {
return SyntaxUsed == AS_ContextSensitiveKeyword;
}
bool isInvalid() const { return Invalid; }

View File

@ -210,6 +210,50 @@ namespace threadSafety {
typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType*, NamedDecl*>,
SourceLocation> UnexpandedParameterPack;
/// Describes whether we've seen any nullability information for the given
/// file.
struct FileNullability {
/// The first pointer declarator (of any pointer kind) in the file that does
/// not have a corresponding nullability annotation.
SourceLocation PointerLoc;
/// Which kind of pointer declarator we saw.
uint8_t PointerKind;
/// Whether we saw any type nullability annotations in the given file.
bool SawTypeNullability = false;
};
/// A mapping from file IDs to a record of whether we've seen nullability
/// information in that file.
class FileNullabilityMap {
/// A mapping from file IDs to the nullability information for each file ID.
llvm::DenseMap<FileID, FileNullability> Map;
/// A single-element cache based on the file ID.
struct {
FileID File;
FileNullability Nullability;
} Cache;
public:
FileNullability &operator[](FileID file) {
// Check the single-element cache.
if (file == Cache.File)
return Cache.Nullability;
// It's not in the single-element cache; flush the cache if we have one.
if (!Cache.File.isInvalid()) {
Map[Cache.File] = Cache.Nullability;
}
// Pull this entry into the cache.
Cache.File = file;
Cache.Nullability = Map[file];
return Cache.Nullability;
}
};
/// Sema - This implements semantic analysis and AST building for C.
class Sema {
Sema(const Sema &) = delete;
@ -341,6 +385,9 @@ public:
PragmaStack<StringLiteral *> ConstSegStack;
PragmaStack<StringLiteral *> CodeSegStack;
/// A mapping that describes the nullability we've seen in each header file.
FileNullabilityMap NullabilityMap;
/// Last section used with #pragma init_seg.
StringLiteral *CurInitSeg;
SourceLocation CurInitSegLoc;

View File

@ -317,14 +317,12 @@ static void addContextSensitiveTypeNullability(Parser &P,
bool &addedToDeclSpec) {
// Create the attribute.
auto getNullabilityAttr = [&]() -> AttributeList * {
auto attr = D.getAttributePool().create(
P.getNullabilityKeyword(nullability),
SourceRange(nullabilityLoc),
nullptr, SourceLocation(),
nullptr, 0,
AttributeList::AS_Keyword);
attr->setContextSensitiveKeywordAttribute();
return attr;
return D.getAttributePool().create(
P.getNullabilityKeyword(nullability),
SourceRange(nullabilityLoc),
nullptr, SourceLocation(),
nullptr, 0,
AttributeList::AS_ContextSensitiveKeyword);
};
if (D.getNumTypeObjects() > 0) {

View File

@ -2759,6 +2759,71 @@ static PointerDeclaratorKind classifyPointerDeclarator(Sema &S,
}
}
static FileID getNullabilityCompletenessCheckFileID(Sema &S,
SourceLocation loc) {
// If we're anywhere in a function, method, or closure context, don't perform
// completeness checks.
for (DeclContext *ctx = S.CurContext; ctx; ctx = ctx->getParent()) {
if (ctx->isFunctionOrMethod())
return FileID();
if (ctx->isFileContext())
break;
}
// We only care about the expansion location.
loc = S.SourceMgr.getExpansionLoc(loc);
FileID file = S.SourceMgr.getFileID(loc);
if (file.isInvalid())
return FileID();
// Retrieve file information.
bool invalid = false;
const SrcMgr::SLocEntry &sloc = S.SourceMgr.getSLocEntry(file, &invalid);
if (invalid || !sloc.isFile())
return FileID();
// We don't want to perform completeness checks on the main file or in
// system headers.
const SrcMgr::FileInfo &fileInfo = sloc.getFile();
if (fileInfo.getIncludeLoc().isInvalid() ||
fileInfo.getFileCharacteristic() != SrcMgr::C_User)
return FileID();
return file;
}
/// Check for consistent use of nullability.
static void checkNullabilityConsistency(TypeProcessingState &state,
SimplePointerKind pointerKind,
SourceLocation pointerLoc) {
Sema &S = state.getSema();
// Determine which file we're performing consistency checking for.
FileID file = getNullabilityCompletenessCheckFileID(S, pointerLoc);
if (file.isInvalid())
return;
// If we haven't seen any type nullability in this file, we won't warn now
// about anything.
FileNullability &fileNullability = S.NullabilityMap[file];
if (!fileNullability.SawTypeNullability) {
// If this is the first pointer declarator in the file, record it.
if (fileNullability.PointerLoc.isInvalid() &&
!S.Context.getDiagnostics().isIgnored(diag::warn_nullability_missing,
pointerLoc)) {
fileNullability.PointerLoc = pointerLoc;
fileNullability.PointerKind = static_cast<unsigned>(pointerKind);
}
return;
}
// Complain about missing nullability.
S.Diag(pointerLoc, diag::warn_nullability_missing)
<< static_cast<unsigned>(pointerKind);
}
static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
QualType declSpecType,
TypeSourceInfo *TInfo) {
@ -2836,6 +2901,22 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
!state.getDeclarator().isObjCWeakProperty() &&
!S.deduceWeakPropertyFromType(T)) {
inAssumeNonNullRegion = true;
// Determine which file we saw the assume-nonnull region in.
FileID file = getNullabilityCompletenessCheckFileID(
S, S.PP.getPragmaAssumeNonNullLoc());
if (!file.isInvalid()) {
FileNullability &fileNullability = S.NullabilityMap[file];
// If we haven't seen any type nullability before, now we have.
if (!fileNullability.SawTypeNullability) {
if (fileNullability.PointerLoc.isValid()) {
S.Diag(fileNullability.PointerLoc, diag::warn_nullability_missing)
<< fileNullability.PointerKind;
}
fileNullability.SawTypeNullability = true;
}
}
}
// Whether to complain about missing nullability specifiers or not.
@ -2857,7 +2938,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// inner pointers.
complainAboutMissingNullability = CAMN_InnerPointers;
if (T->canHaveNullability()) {
if (T->canHaveNullability() && !T->getNullability(S.Context)) {
++NumPointersRemaining;
}
@ -2967,27 +3048,42 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// If we're supposed to infer nullability, do so now.
if (inferNullability) {
auto syntax = inferNullabilityCS ? AttributeList::AS_ContextSensitiveKeyword
: AttributeList::AS_Keyword;
AttributeList *nullabilityAttr = state.getDeclarator().getAttributePool()
.create(
S.getNullabilityKeyword(
*inferNullability),
SourceRange(pointerLoc),
nullptr, SourceLocation(),
nullptr, 0,
AttributeList::AS_Keyword);
if (inferNullabilityCS)
nullabilityAttr->setContextSensitiveKeywordAttribute();
nullptr, 0, syntax);
spliceAttrIntoList(*nullabilityAttr, attrs);
return nullabilityAttr;
}
// If we're supposed to complain about missing nullability, do so
// now if it's truly missing.
switch (complainAboutMissingNullability) {
case CAMN_No:
break;
case CAMN_InnerPointers:
if (NumPointersRemaining == 0)
break;
// Fallthrough.
case CAMN_Yes:
checkNullabilityConsistency(state, pointerKind, pointerLoc);
}
return nullptr;
};
// If the type itself could have nullability but does not, infer pointer
// nullability.
if (T->canHaveNullability() && S.ActiveTemplateInstantiations.empty()) {
// nullability and perform consistency checking.
if (T->canHaveNullability() && S.ActiveTemplateInstantiations.empty() &&
!T->getNullability(S.Context)) {
SimplePointerKind pointerKind = SimplePointerKind::Pointer;
if (T->isBlockPointerType())
pointerKind = SimplePointerKind::BlockPointer;
@ -4919,10 +5015,27 @@ static bool handleMSPointerTypeQualifierAttr(TypeProcessingState &State,
return false;
}
bool Sema::checkNullabilityTypeSpecifier(QualType &type,
bool Sema::checkNullabilityTypeSpecifier(QualType &type,
NullabilityKind nullability,
SourceLocation nullabilityLoc,
bool isContextSensitive) {
// We saw a nullability type specifier. If this is the first one for
// this file, note that.
FileID file = getNullabilityCompletenessCheckFileID(*this, nullabilityLoc);
if (!file.isInvalid()) {
FileNullability &fileNullability = NullabilityMap[file];
if (!fileNullability.SawTypeNullability) {
// If we have already seen a pointer declarator without a nullability
// annotation, complain about it.
if (fileNullability.PointerLoc.isValid()) {
Diag(fileNullability.PointerLoc, diag::warn_nullability_missing)
<< fileNullability.PointerKind;
}
fileNullability.SawTypeNullability = true;
}
}
// Check for existing nullability attributes on the type.
QualType desugared = type;
while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) {

View File

@ -0,0 +1,17 @@
void f1(int *ptr); // expected-warning{{pointer is missing a nullability type specifier}}
void f2(int * __nonnull);
#include "nullability-consistency-2.h"
void f3(int *ptr) { // expected-warning{{pointer is missing a nullability type specifier}}
int *other = ptr; // shouldn't warn
}
class X {
void mf(int *ptr); // expected-warning{{pointer is missing a nullability type specifier}}
int X:: *memptr; // expected-warning{{member pointer is missing a nullability type specifier}}
};

View File

@ -0,0 +1,16 @@
void g1(int * __nonnull);
void g2(int (^block)(int, int)); // expected-warning{{block pointer is missing a nullability type specifier}}
void g3(const
id // expected-warning{{missing a nullability type specifier}}
volatile
* // expected-warning{{missing a nullability type specifier}}
);
@interface SomeClass
@property (retain,nonnull) id property1;
@property (retain,nullable) SomeClass *property2;
- (nullable SomeClass *)method1;
- (void)method2:(nonnull SomeClass *)param;
@end

View File

@ -0,0 +1 @@
void double_declarator1(int *__nonnull *); // expected-warning{{pointer is missing a nullability type specifier (__nonnull, __nullable, or __null_unspecified)}}

View File

@ -0,0 +1 @@
void double_declarator1(int * * __nonnull); // expected-warning{{pointer is missing a nullability type specifier (__nonnull, __nullable, or __null_unspecified)}}

View File

@ -0,0 +1,14 @@
#define SUPPRESS_NULLABILITY_WARNING(Type) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wnullability-completeness\"") \
Type \
_Pragma("clang diagnostic pop")
void suppress1(SUPPRESS_NULLABILITY_WARNING(int *) ptr); // no warning
void shouldwarn5(int *ptr); //expected-warning{{missing a nullability type specifier}}
void trigger5(int * __nonnull);
void suppress2(SUPPRESS_NULLABILITY_WARNING(int *) ptr); // no warning

View File

@ -0,0 +1,8 @@
int *ptr; // expected-warning {{missing a nullability type specifier}}
#pragma clang assume_nonnull begin
extern void **blah; // expected-warning 2{{missing a nullability type specifier}}
#pragma clang assume_nonnull end

View File

@ -0,0 +1,40 @@
#ifndef SOMEKIT_H
#define SOMEKIT_H
__attribute__((objc_root_class))
#ifndef NS_ASSUME_NONNULL_BEGIN
#if __has_feature(assume_nonnull)
#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin")
#define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end")
#else
#define NS_ASSUME_NONNULL_BEGIN
#define NS_ASSUME_NONNULL_END
#endif
#endif
NS_ASSUME_NONNULL_BEGIN
@interface A
-(null_unspecified A*)transform:(null_unspecified A*)input __attribute__((unavailable("anything but this")));
-(A*)transform:(A*)input integer:(int)integer;
@property (null_unspecified, nonatomic, readonly, retain) A* someA;
@property (null_unspecified, nonatomic, retain) A* someOtherA;
@property (nonatomic) int intValue __attribute__((unavailable("wouldn't work anyway")));
@end
NS_ASSUME_NONNULL_END
__attribute__((unavailable("just don't")))
@interface B : A
@end
@interface C : A
- (instancetype)init; // expected-warning{{pointer is missing a nullability type specifier}}
- (instancetype)initWithA:( A*)a __attribute__((objc_designated_initializer)); // expected-warning 2{{pointer is missing a nullability type specifier}}
@end
#endif

View File

@ -0,0 +1,11 @@
typedef int* __nonnull mynonnull;
__attribute__((objc_root_class))
@interface typedefClass
- (void) func1:(mynonnull)i;
@end
void func2(mynonnull i);
void func3(int *); // expected-warning{{pointer is missing a nullability type specifier}}

View File

@ -8,7 +8,7 @@ __attribute__((objc_root_class))
struct X { };
void f1(int *x);
void f1(int *x); // expected-warning{{pointer is missing a nullability type specifier}}
typedef struct __attribute__((objc_bridge(NSError))) __CFError *CFErrorRef;
typedef NSError *NSErrorPtr;
@ -38,14 +38,16 @@ A *f14(void);
int * __null_unspecified f15(void);
A * __null_unspecified f16(void);
void f17(CFErrorRef *error); // expected-note{{no known conversion from 'A * __nonnull' to 'CFErrorRef __nullable * __nullable' (aka '__CFError **') for 1st argument}}
void f18(A **);
void f19(CFErrorRefPtr error);
void f18(A **); // expected-warning 2{{pointer is missing a nullability type specifier}}
void f19(CFErrorRefPtr error); // expected-warning{{pointer is missing a nullability type specifier}}
void g1(int (^)(int, int));
void g2(int (^ *bp)(int, int));
void g3(block_ptr *bp);
void g2(int (^ *bp)(int, int)); // expected-warning{{block pointer is missing a nullability type specifier}}
// expected-warning@-1{{pointer is missing a nullability type specifier}}
void g3(block_ptr *bp); // expected-warning{{block pointer is missing a nullability type specifier}}
// expected-warning@-1{{pointer is missing a nullability type specifier}}
void g4(int (*fp)(int, int));
void g5(int (**fp)(int, int));
void g5(int (**fp)(int, int)); // expected-warning 2{{pointer is missing a nullability type specifier}}
@interface A(Pragmas1)
+ (instancetype)aWithA:(A *)a;
@ -54,9 +56,10 @@ void g5(int (**fp)(int, int));
- (void)method3:(NSError **)error; // expected-note{{passing argument to parameter 'error' here}}
- (void)method4:(NSErrorPtr *)error; // expected-note{{passing argument to parameter 'error' here}}
- (void)method5:(NSErrorPtrPtr)error;
// expected-warning@-1{{pointer is missing a nullability type specifier}}
@property A *aProp;
@property NSError **anError;
@property NSError **anError; // expected-warning 2{{pointer is missing a nullability type specifier}}
@end
int *global_int_ptr;
@ -64,7 +67,7 @@ int *global_int_ptr;
// typedefs not inferred __nonnull
typedef int *int_ptr_2;
typedef int *
typedef int * // expected-warning{{pointer is missing a nullability type specifier}}
*int_ptr_ptr;
static inline void f30(void) {
@ -86,13 +89,13 @@ static inline void f30(void) {
#pragma clang assume_nonnull end
void f20(A *a);
void f21(int_ptr x);
void f22(A_ptr y);
void f20(A *a); // expected-warning{{pointer is missing a nullability type specifier}}
void f21(int_ptr x); // expected-warning{{pointer is missing a nullability type specifier}}
void f22(A_ptr y); // expected-warning{{pointer is missing a nullability type specifier}}
void f23(int_ptr __nullable x);
void f24(A_ptr __nullable y);
void f25(int_ptr_2 x);
void f25(int_ptr_2 x); // expected-warning{{pointer is missing a nullability type specifier}}
@interface A(OutsidePragmas1)
+ (instancetype)aWithInt:(int)value;
+ (instancetype)aWithInt:(int)value; // expected-warning{{pointer is missing a nullability type specifier}}
@end

View File

@ -0,0 +1,14 @@
// RUN: %clang_cc1 -fsyntax-only -fblocks -I %S/Inputs %s -verify
#include "nullability-consistency-1.h"
#include "nullability-consistency-3.h"
#include "nullability-consistency-4.h"
#include "nullability-consistency-5.h"
#include "nullability-consistency-5.h"
#include "nullability-consistency-6.h"
#include "nullability-consistency-7.h"
#include "nullability-consistency-8.h"
void h1(int *ptr) { } // don't warn
void h2(int * __nonnull) { }

View File

@ -2721,7 +2721,8 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) {
StringMatcher("Name", Declspec, OS).Emit();
OS << " } else if (AttributeList::AS_CXX11 == Syntax) {\n";
StringMatcher("Name", CXX11, OS).Emit();
OS << " } else if (AttributeList::AS_Keyword == Syntax) {\n";
OS << " } else if (AttributeList::AS_Keyword == Syntax || ";
OS << "AttributeList::AS_ContextSensitiveKeyword == Syntax) {\n";
StringMatcher("Name", Keywords, OS).Emit();
OS << " } else if (AttributeList::AS_Pragma == Syntax) {\n";
StringMatcher("Name", Pragma, OS).Emit();