diff options
author | Caitlin Sadowski <supertri@google.com> | 2011-09-08 17:42:22 +0000 |
---|---|---|
committer | Caitlin Sadowski <supertri@google.com> | 2011-09-08 17:42:22 +0000 |
commit | eff98fc3561f6b717f6348f04b3f4fe03e934466 (patch) | |
tree | afe927f3c46c971b5e38f83f21da5b6fd6b76bd5 | |
parent | aebb653a28b142b570596e45cb31b1d5fce1710c (diff) |
Thread Safety: Patch to implement delayed parsing of attributes within a
class scope.
This patch was also written by DeLesley Hutchins.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@139301 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/Attr.td | 17 | ||||
-rw-r--r-- | include/clang/CMakeLists.txt | 1 | ||||
-rw-r--r-- | include/clang/Makefile | 2 | ||||
-rw-r--r-- | include/clang/Parse/CMakeLists.txt | 4 | ||||
-rw-r--r-- | include/clang/Parse/Makefile | 13 | ||||
-rw-r--r-- | include/clang/Parse/Parser.h | 48 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 4 | ||||
-rw-r--r-- | lib/Parse/CMakeLists.txt | 2 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 400 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 25 | ||||
-rw-r--r-- | lib/Sema/AnalysisBasedWarnings.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 9 | ||||
-rw-r--r-- | test/SemaCXX/warn-thread-safety-analysis.cpp | 25 | ||||
-rw-r--r-- | test/SemaCXX/warn-thread-safety-parsing.cpp | 22 |
14 files changed, 421 insertions, 153 deletions
diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index fb3ba02575..d2de30a439 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -90,7 +90,9 @@ class Attr { // The attribute will not be permitted in C++0x attribute-specifiers if // this is empty; the empty string can be used as a namespace. list<string> Namespaces = []; - // Any additional text that should be included verbatim in the class. + // Set to true for attributes with arguments which require delayed parsing. + bit LateParsed = 0; + // Any additional text that should be included verbatim in the class. code AdditionalMembers = [{}]; } @@ -566,64 +568,77 @@ def NoThreadSafetyAnalysis : InheritableAttr { def GuardedBy : InheritableAttr { let Spellings = ["guarded_by"]; let Args = [ExprArgument<"Arg">]; + let LateParsed = 1; } def PtGuardedBy : InheritableAttr { let Spellings = ["pt_guarded_by"]; let Args = [ExprArgument<"Arg">]; + let LateParsed = 1; } def AcquiredAfter : InheritableAttr { let Spellings = ["acquired_after"]; let Args = [VariadicExprArgument<"Args">]; + let LateParsed = 1; } def AcquiredBefore : InheritableAttr { let Spellings = ["acquired_before"]; let Args = [VariadicExprArgument<"Args">]; + let LateParsed = 1; } def ExclusiveLockFunction : InheritableAttr { let Spellings = ["exclusive_lock_function"]; let Args = [VariadicExprArgument<"Args">]; + let LateParsed = 1; } def SharedLockFunction : InheritableAttr { let Spellings = ["shared_lock_function"]; let Args = [VariadicExprArgument<"Args">]; + let LateParsed = 1; } def ExclusiveTrylockFunction : InheritableAttr { let Spellings = ["exclusive_trylock_function"]; let Args = [VariadicExprArgument<"Args">]; + let LateParsed = 1; } def SharedTrylockFunction : InheritableAttr { let Spellings = ["shared_trylock_function"]; let Args = [VariadicExprArgument<"Args">]; + let LateParsed = 1; } def UnlockFunction : InheritableAttr { let Spellings = ["unlock_function"]; let Args = [VariadicExprArgument<"Args">]; + let LateParsed = 1; } def LockReturned : InheritableAttr { let Spellings = ["lock_returned"]; let Args = [ExprArgument<"Arg">]; + let LateParsed = 1; } def LocksExcluded : InheritableAttr { let Spellings = ["locks_excluded"]; let Args = [VariadicExprArgument<"Args">]; + let LateParsed = 1; } def ExclusiveLocksRequired : InheritableAttr { let Spellings = ["exclusive_locks_required"]; let Args = [VariadicExprArgument<"Args">]; + let LateParsed = 1; } def SharedLocksRequired : InheritableAttr { let Spellings = ["shared_locks_required"]; let Args = [VariadicExprArgument<"Args">]; + let LateParsed = 1; } diff --git a/include/clang/CMakeLists.txt b/include/clang/CMakeLists.txt index 375ae5bdab..fb4e04d55b 100644 --- a/include/clang/CMakeLists.txt +++ b/include/clang/CMakeLists.txt @@ -2,4 +2,5 @@ add_subdirectory(AST) add_subdirectory(Basic) add_subdirectory(Driver) add_subdirectory(Lex) +add_subdirectory(Parse) add_subdirectory(Serialization) diff --git a/include/clang/Makefile b/include/clang/Makefile index a7be0319e5..a6f2597cb9 100644 --- a/include/clang/Makefile +++ b/include/clang/Makefile @@ -1,5 +1,5 @@ CLANG_LEVEL := ../.. -DIRS := AST Basic Driver Lex Serialization +DIRS := AST Basic Driver Lex Parse Serialization include $(CLANG_LEVEL)/Makefile diff --git a/include/clang/Parse/CMakeLists.txt b/include/clang/Parse/CMakeLists.txt new file mode 100644 index 0000000000..d1ff2abfee --- /dev/null +++ b/include/clang/Parse/CMakeLists.txt @@ -0,0 +1,4 @@ +clang_tablegen(AttrLateParsed.inc -gen-clang-attr-late-parsed-list + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ + SOURCE ../Basic/Attr.td + TARGET ClangAttrLateParsed) diff --git a/include/clang/Parse/Makefile b/include/clang/Parse/Makefile new file mode 100644 index 0000000000..b1c81dfe77 --- /dev/null +++ b/include/clang/Parse/Makefile @@ -0,0 +1,13 @@ +CLANG_LEVEL := ../../.. +TD_SRC_DIR = $(PROJ_SRC_DIR)/../Basic +BUILT_SOURCES = AttrLateParsed.inc + +TABLEGEN_INC_FILES_COMMON = 1 + +include $(CLANG_LEVEL)/Makefile + +$(ObjDir)/AttrLateParsed.inc.tmp : $(TD_SRC_DIR)/Attr.td $(TBLGEN) \ + $(ObjDir)/.dir + $(Echo) "Building Clang attribute late-parsed table with tblgen" + $(Verb) $(TableGen) -gen-clang-attr-late-parsed-list -o $(call SYSPATH, $@) \ + -I $(PROJ_SRC_DIR)/../../ $<
\ No newline at end of file diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 1b0949c324..71042303ba 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -22,6 +22,7 @@ #include "clang/Sema/DeclSpec.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallVector.h" #include <stack> namespace clang { @@ -664,6 +665,7 @@ private: virtual void ParseLexedMethodDeclarations(); virtual void ParseLexedMemberInitializers(); virtual void ParseLexedMethodDefs(); + virtual void ParseLexedAttributes(); }; /// Inner node of the LateParsedDeclaration tree that parses @@ -676,12 +678,39 @@ private: virtual void ParseLexedMethodDeclarations(); virtual void ParseLexedMemberInitializers(); virtual void ParseLexedMethodDefs(); + virtual void ParseLexedAttributes(); private: Parser *Self; ParsingClass *Class; }; + /// Contains the lexed tokens of an attribute with arguments that + /// may reference member variables and so need to be parsed at the + /// end of the class declaration after parsing all other member + /// member declarations. + /// FIXME: Perhaps we should change the name of LateParsedDeclaration to + /// LateParsedTokens. + struct LateParsedAttribute : public LateParsedDeclaration { + Parser *Self; + CachedTokens Toks; + IdentifierInfo &AttrName; + SourceLocation AttrNameLoc; + Decl *D; + + explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name, + SourceLocation Loc) + : Self(P), AttrName(Name), AttrNameLoc(Loc), D(0) {} + + virtual void ParseLexedAttributes(); + + void setDecl(Decl *Dec) { D = Dec; } + }; + + /// A list of late parsed attributes. Used by ParseGNUAttributes. + typedef llvm::SmallVector<LateParsedAttribute*, 2> LateParsedAttrList; + + /// Contains the lexed tokens of a member function definition /// which needs to be parsed at the end of the class declaration /// after parsing all other member declarations. @@ -1028,6 +1057,8 @@ private: const ParsedTemplateInfo &TemplateInfo, const VirtSpecifiers& VS, ExprResult& Init); void ParseCXXNonStaticMemberInitializer(Decl *VarD); + void ParseLexedAttributes(ParsingClass &Class); + void ParseLexedAttribute(LateParsedAttribute &LA); void ParseLexedMethodDeclarations(ParsingClass &Class); void ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM); void ParseLexedMethodDefs(ParsingClass &Class); @@ -1665,21 +1696,28 @@ bool ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo *> &Names, } void DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs); - void MaybeParseGNUAttributes(Declarator &D) { + void MaybeParseGNUAttributes(Declarator &D, + LateParsedAttrList *LateAttrs = 0) { if (Tok.is(tok::kw___attribute)) { ParsedAttributes attrs(AttrFactory); SourceLocation endLoc; - ParseGNUAttributes(attrs, &endLoc); + ParseGNUAttributes(attrs, &endLoc, LateAttrs); D.takeAttributes(attrs, endLoc); } } void MaybeParseGNUAttributes(ParsedAttributes &attrs, - SourceLocation *endLoc = 0) { + SourceLocation *endLoc = 0, + LateParsedAttrList *LateAttrs = 0) { if (Tok.is(tok::kw___attribute)) - ParseGNUAttributes(attrs, endLoc); + ParseGNUAttributes(attrs, endLoc, LateAttrs); } void ParseGNUAttributes(ParsedAttributes &attrs, - SourceLocation *endLoc = 0); + SourceLocation *endLoc = 0, + LateParsedAttrList *LateAttrs = 0); + void ParseGNUAttributeArgs(IdentifierInfo *AttrName, + SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, + SourceLocation *EndLoc); void MaybeParseCXX0XAttributes(Declarator &D) { if (getLang().CPlusPlus0x && isCXX0XAttributeSpecifier()) { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index fce56f0621..89d210f135 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1064,6 +1064,10 @@ public: Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body); Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body, bool IsInstantiation); + /// ActOnFinishDelayedAttribute - Invoked when we have finished parsing an + /// attribute for which parsing is delayed. + void ActOnFinishDelayedAttribute(Scope *S, Decl *D, ParsedAttributes &Attrs); + /// \brief Diagnose any unused parameters in the given sequence of /// ParmVarDecl pointers. void DiagnoseUnusedParameters(ParmVarDecl * const *Begin, diff --git a/lib/Parse/CMakeLists.txt b/lib/Parse/CMakeLists.txt index 6bf5e64cc6..6c980ced7e 100644 --- a/lib/Parse/CMakeLists.txt +++ b/lib/Parse/CMakeLists.txt @@ -16,4 +16,4 @@ add_clang_library(clangParse Parser.cpp ) -add_dependencies(clangParse ClangAttrClasses ClangAttrList ClangDeclNodes ClangDiagnosticParse ClangStmtNodes) +add_dependencies(clangParse ClangAttrClasses ClangAttrList ClangDeclNodes ClangDiagnosticParse ClangStmtNodes ClangAttrLateParsed) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 40674ee7a9..1f9f85bab7 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -55,6 +55,16 @@ TypeResult Parser::ParseTypeName(SourceRange *Range, return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); } + +/// isAttributeLateParsed - Return true if the attribute has arguments that +/// require late parsing. +static bool isAttributeLateParsed(const IdentifierInfo &II) { + return llvm::StringSwitch<bool>(II.getName()) +#include "clang/Parse/AttrLateParsed.inc" + .Default(false); +} + + /// ParseGNUAttributes - Parse a non-empty attributes list. /// /// [GNU] attributes: @@ -92,7 +102,8 @@ TypeResult Parser::ParseTypeName(SourceRange *Range, /// a pressing need to implement the 2 token lookahead. void Parser::ParseGNUAttributes(ParsedAttributes &attrs, - SourceLocation *endLoc) { + SourceLocation *endLoc, + LateParsedAttrList *LateAttrs) { assert(Tok.is(tok::kw___attribute) && "Not a GNU attribute list!"); while (Tok.is(tok::kw___attribute)) { @@ -109,7 +120,6 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs, // Parse the attribute-list. e.g. __attribute__(( weak, alias("__f") )) while (Tok.is(tok::identifier) || isDeclarationSpecifier() || Tok.is(tok::comma)) { - if (Tok.is(tok::comma)) { // allows for empty/non-empty attributes. ((__vector_size__(16),,,,)) ConsumeToken(); @@ -119,116 +129,26 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs, IdentifierInfo *AttrName = Tok.getIdentifierInfo(); SourceLocation AttrNameLoc = ConsumeToken(); - // Availability attributes have their own grammar. - if (AttrName->isStr("availability")) - ParseAvailabilityAttribute(*AttrName, AttrNameLoc, attrs, endLoc); - // Thread safety attributes fit into the FIXME case above, so we - // just parse the arguments as a list of expressions - else if (IsThreadSafetyAttribute(AttrName->getName())) - ParseThreadSafetyAttribute(*AttrName, AttrNameLoc, attrs, endLoc); - // check if we have a "parameterized" attribute - else if (Tok.is(tok::l_paren)) { - ConsumeParen(); // ignore the left paren loc for now - - if (Tok.is(tok::identifier)) { - IdentifierInfo *ParmName = Tok.getIdentifierInfo(); - SourceLocation ParmLoc = ConsumeToken(); - - if (Tok.is(tok::r_paren)) { - // __attribute__(( mode(byte) )) - ConsumeParen(); // ignore the right paren loc for now - attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, - ParmName, ParmLoc, 0, 0); - } else if (Tok.is(tok::comma)) { - ConsumeToken(); - // __attribute__(( format(printf, 1, 2) )) - ExprVector ArgExprs(Actions); - bool ArgExprsOk = true; - - // now parse the non-empty comma separated list of expressions - while (1) { - ExprResult ArgExpr(ParseAssignmentExpression()); - if (ArgExpr.isInvalid()) { - ArgExprsOk = false; - SkipUntil(tok::r_paren); - break; - } else { - ArgExprs.push_back(ArgExpr.release()); - } - if (Tok.isNot(tok::comma)) - break; - ConsumeToken(); // Eat the comma, move to the next argument - } - if (ArgExprsOk && Tok.is(tok::r_paren)) { - ConsumeParen(); // ignore the right paren loc for now - attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, - ParmName, ParmLoc, ArgExprs.take(), ArgExprs.size()); - } - } - } else { // not an identifier - switch (Tok.getKind()) { - case tok::r_paren: - // parse a possibly empty comma separated list of expressions - // __attribute__(( nonnull() )) - ConsumeParen(); // ignore the right paren loc for now - attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, - 0, SourceLocation(), 0, 0); - break; - case tok::kw_char: - case tok::kw_wchar_t: - case tok::kw_char16_t: - case tok::kw_char32_t: - case tok::kw_bool: - case tok::kw_short: - case tok::kw_int: - case tok::kw_long: - case tok::kw___int64: - case tok::kw_signed: - case tok::kw_unsigned: - case tok::kw_float: - case tok::kw_double: - case tok::kw_void: - case tok::kw_typeof: { - AttributeList *attr - = attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, - 0, SourceLocation(), 0, 0); - if (attr->getKind() == AttributeList::AT_IBOutletCollection) - Diag(Tok, diag::err_iboutletcollection_builtintype); - // If it's a builtin type name, eat it and expect a rparen - // __attribute__(( vec_type_hint(char) )) - ConsumeToken(); - if (Tok.is(tok::r_paren)) - ConsumeParen(); - break; - } - default: - // __attribute__(( aligned(16) )) - ExprVector ArgExprs(Actions); - bool ArgExprsOk = true; - - // now parse the list of expressions - while (1) { - ExprResult ArgExpr(ParseAssignmentExpression()); - if (ArgExpr.isInvalid()) { - ArgExprsOk = false; - SkipUntil(tok::r_paren); - break; - } else { - ArgExprs.push_back(ArgExpr.release()); - } - if (Tok.isNot(tok::comma)) - break; - ConsumeToken(); // Eat the comma, move to the next argument - } - // Match the ')'. - if (ArgExprsOk && Tok.is(tok::r_paren)) { - ConsumeParen(); // ignore the right paren loc for now - attrs.addNew(AttrName, AttrNameLoc, 0, - AttrNameLoc, 0, SourceLocation(), - ArgExprs.take(), ArgExprs.size()); - } - break; - } + if (Tok.is(tok::l_paren)) { + // handle "parameterized" attributes + if (LateAttrs && !ClassStack.empty() && + isAttributeLateParsed(*AttrName)) { + // Delayed parsing is only available for attributes that occur + // in certain locations within a class scope. + LateParsedAttribute *LA = + new LateParsedAttribute(this, *AttrName, AttrNameLoc); + LateAttrs->push_back(LA); + getCurrentClass().LateParsedDeclarations.push_back(LA); + + // consume everything up to and including the matching right parens + ConsumeAndStoreUntil(tok::r_paren, LA->Toks, true, false); + + Token Eof; + Eof.startToken(); + Eof.setLocation(Tok.getLocation()); + LA->Toks.push_back(Eof); + } else { + ParseGNUAttributeArgs(AttrName, AttrNameLoc, attrs, endLoc); } } else { attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, @@ -246,6 +166,132 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs, } } + +/// Parse the arguments to a parameterized GNU attribute +void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName, + SourceLocation AttrNameLoc, + ParsedAttributes &Attrs, + SourceLocation *EndLoc) { + + assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); + + // Availability attributes have their own grammar. + if (AttrName->isStr("availability")) { + ParseAvailabilityAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc); + return; + } + // Thread safety attributes fit into the FIXME case above, so we + // just parse the arguments as a list of expressions + if (IsThreadSafetyAttribute(AttrName->getName())) { + ParseThreadSafetyAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc); + return; + } + + ConsumeParen(); // ignore the left paren loc for now + + if (Tok.is(tok::identifier)) { + IdentifierInfo *ParmName = Tok.getIdentifierInfo(); + SourceLocation ParmLoc = ConsumeToken(); + + if (Tok.is(tok::r_paren)) { + // __attribute__(( mode(byte) )) + ConsumeParen(); // ignore the right paren loc for now + Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, + ParmName, ParmLoc, 0, 0); + } else if (Tok.is(tok::comma)) { + ConsumeToken(); + // __attribute__(( format(printf, 1, 2) )) + ExprVector ArgExprs(Actions); + bool ArgExprsOk = true; + + // now parse the non-empty comma separated list of expressions + while (1) { + ExprResult ArgExpr(ParseAssignmentExpression()); + if (ArgExpr.isInvalid()) { + ArgExprsOk = false; + SkipUntil(tok::r_paren); + break; + } else { + ArgExprs.push_back(ArgExpr.release()); + } + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // Eat the comma, move to the next argument + } + if (ArgExprsOk && Tok.is(tok::r_paren)) { + ConsumeParen(); // ignore the right paren loc for now + Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, + ParmName, ParmLoc, ArgExprs.take(), ArgExprs.size()); + } + } + } else { // not an identifier + switch (Tok.getKind()) { + case tok::r_paren: + // parse a possibly empty comma separated list of expressions + // __attribute__(( nonnull() )) + ConsumeParen(); // ignore the right paren loc for now + Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, + 0, SourceLocation(), 0, 0); + break; + case tok::kw_char: + case tok::kw_wchar_t: + case tok::kw_char16_t: + case tok::kw_char32_t: + case tok::kw_bool: + case tok::kw_short: + case tok::kw_int: + case tok::kw_long: + case tok::kw___int64: + case tok::kw_signed: + case tok::kw_unsigned: + case tok::kw_float: + case tok::kw_double: + case tok::kw_void: + case tok::kw_typeof: { + AttributeList *attr + = Attrs.addNew(AttrName, AttrNameLoc, 0, AttrNameLoc, + 0, SourceLocation(), 0, 0); + if (attr->getKind() == AttributeList::AT_IBOutletCollection) + Diag(Tok, diag::err_iboutletcollection_builtintype); + // If it's a builtin type name, eat it and expect a rparen + // __attribute__(( vec_type_hint(char) )) + ConsumeToken(); + if (Tok.is(tok::r_paren)) + ConsumeParen(); + break; + } + default: + // __attribute__(( aligned(16) )) + ExprVector ArgExprs(Actions); + bool ArgExprsOk = true; + + // now parse the list of expressions + while (1) { + ExprResult ArgExpr(ParseAssignmentExpression()); + if (ArgExpr.isInvalid()) { + ArgExprsOk = false; + SkipUntil(tok::r_paren); + break; + } else { + ArgExprs.push_back(ArgExpr.release()); + } + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // Eat the comma, move to the next argument + } + // Match the ')'. + if (ArgExprsOk && Tok.is(tok::r_paren)) { + ConsumeParen(); // ignore the right paren loc for now + Attrs.addNew(AttrName, AttrNameLoc, 0, + AttrNameLoc, 0, SourceLocation(), + ArgExprs.take(), ArgExprs.size()); + } + break; + } + } +} + + /// ParseMicrosoftDeclSpec - Parse an __declspec construct /// /// [MS] decl-specifier: @@ -658,6 +704,80 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, UnavailableLoc, false, false); } + +// Late Parsed Attributes: +// See other examples of late parsing in lib/Parse/ParseCXXInlineMethods + +void Parser::LateParsedDeclaration::ParseLexedAttributes() {} + +void Parser::LateParsedClass::ParseLexedAttributes() { + Self->ParseLexedAttributes(*Class); +} + +void Parser::LateParsedAttribute::ParseLexedAttributes() { + Self->ParseLexedAttribute(*this); +} + +/// Wrapper class which calls ParseLexedAttribute, after setting up the +/// scope appropriately. +void Parser::ParseLexedAttributes(ParsingClass &Class) { + // Deal with templates + // FIXME: Test cases to make sure this does the right thing for templates. + bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; + ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, + HasTemplateScope); + if (HasTemplateScope) + Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); + + // Set or update the scope flags to include Scope::ThisScope. + bool AlreadyHasClassScope = Class.TopLevelClass; + unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope|Scope::ThisScope; + ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope); + ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope); + + for (unsigned i = 0, ni = Class.LateParsedDeclarations.size(); i < ni; ++i) { + Class.LateParsedDeclarations[i]->ParseLexedAttributes(); + } +} + +/// \brief Finish parsing an attribute for which parsing was delayed. +/// This will be called at the end of parsing a class declaration +/// for each LateParsedAttribute. We consume the saved tokens and +/// create an attribute with the arguments filled in. We add this +/// to the Attribute list for the decl. +void Parser::ParseLexedAttribute(LateParsedAttribute &LA) { + // Save the current token position. + SourceLocation OrigLoc = Tok.getLocation(); + + // Append the current token at the end of the new token stream so that it + // doesn't get lost. + LA.Toks.push_back(Tok); + PP.EnterTokenStream(LA.Toks.data(), LA.Toks.size(), true, false); + // Consume the previously pushed token. + ConsumeAnyToken(); + + ParsedAttributes Attrs(AttrFactory); + SourceLocation endLoc; + + ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc); + + // Late parsed attributes must be attached to Decls by hand. If the + // LA.D is not set, then this was not done properly. + assert(LA.D && "No decl attached to late parsed attribute"); + Actions.ActOnFinishDelayedAttribute(getCurScope(), LA.D, Attrs); + + if (Tok.getLocation() != OrigLoc) { + // Due to a parsing error, we either went over the cached tokens or + // there are still cached tokens left, so we skip the leftover tokens. + // Since this is an uncommon situation that should be avoided, use the + // expensive isBeforeInTranslationUnit call. + if (PP.getSourceManager().isBeforeInTranslationUnit(Tok.getLocation(), + OrigLoc)) + while (Tok.getLocation() != OrigLoc && Tok.isNot(tok::eof)) + ConsumeAnyToken(); + } +} + /// \brief Wrapper around a case statement checking if AttrName is /// one of the thread safety attributes bool Parser::IsThreadSafetyAttribute(llvm::StringRef AttrName){ @@ -699,37 +819,35 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc) { + assert(Tok.is(tok::l_paren) && "Attribute arg list not starting with '('"); - if (Tok.is(tok::l_paren)) { - SourceLocation LeftParenLoc = Tok.getLocation(); - ConsumeParen(); // ignore the left paren loc for now - - ExprVector ArgExprs(Actions); - bool ArgExprsOk = true; - - // now parse the list of expressions - while (1) { - ExprResult ArgExpr(ParseAssignmentExpression()); - if (ArgExpr.isInvalid()) { - ArgExprsOk = false; - MatchRHSPunctuation(tok::r_paren, LeftParenLoc); - break; - } else { - ArgExprs.push_back(ArgExpr.release()); - } - if (Tok.isNot(tok::comma)) - break; - ConsumeToken(); // Eat the comma, move to the next argument - } - // Match the ')'. - if (ArgExprsOk && Tok.is(tok::r_paren)) { - ConsumeParen(); // ignore the right paren loc for now - Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(), - ArgExprs.take(), ArgExprs.size()); + SourceLocation LeftParenLoc = Tok.getLocation(); + ConsumeParen(); + + ExprVector ArgExprs(Actions); + bool ArgExprsOk = true; + + // now parse the list of expressions + while (1) { + ExprResult ArgExpr(ParseAssignmentExpression()); + if (ArgExpr.isInvalid()) { + ArgExprsOk = false; + MatchRHSPunctuation(tok::r_paren, LeftParenLoc); + break; + } else { + ArgExprs.push_back(ArgExpr.release()); } - } else { - Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, - 0, SourceLocation(), 0, 0); + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); // Eat the comma, move to the next argument + } + // Match the ')'. + if (ArgExprsOk && Tok.is(tok::r_paren)) { + if (EndLoc) + *EndLoc = Tok.getLocation(); + ConsumeParen(); + Attrs.addNew(&AttrName, AttrNameLoc, 0, AttrNameLoc, 0, SourceLocation(), + ArgExprs.take(), ArgExprs.size()); } } diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 385683abd6..0dacf3c8a3 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -1707,6 +1707,9 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, VirtSpecifiers VS; ExprResult Init; + // Hold late-parsed attributes so we can attach a Decl to them later. + LateParsedAttrList LateParsedAttrs; + if (Tok.isNot(tok::colon)) { // Don't parse FOO:BAR as if it were a typo for FOO::BAR. ColonProtectionRAIIObject X(*this); @@ -1725,7 +1728,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, ParseOptionalCXX0XVirtSpecifierSeq(VS); // If attributes exist after the declarator, but before an '{', parse them. - MaybeParseGNUAttributes(DeclaratorInfo); + MaybeParseGNUAttributes(DeclaratorInfo, &LateParsedAttrs); // MSVC permits pure specifier on inline functions declared at class scope. // Hence check for =0 before checking for function definition. @@ -1782,7 +1785,13 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, return; } - ParseCXXInlineMethodDef(AS, DeclaratorInfo, TemplateInfo, VS, Init); + Decl *FunDecl = + ParseCXXInlineMethodDef(AS, DeclaratorInfo, TemplateInfo, VS, Init); + + for (unsigned i = 0, ni = LateParsedAttrs.size(); i < ni; ++i) { + LateParsedAttrs[i]->setDecl(FunDecl); + } + LateParsedAttrs.clear(); // Consume the ';' - it's optional unless we have a delete or default if (Tok.is(tok::semi)) { @@ -1824,7 +1833,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, } // If attributes exist after the declarator, parse them. - MaybeParseGNUAttributes(DeclaratorInfo); + MaybeParseGNUAttributes(DeclaratorInfo, &LateParsedAttrs); // FIXME: When g++ adds support for this, we'll need to check whether it // goes before or after the GNU attributes and __asm__. @@ -1882,6 +1891,12 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, DeclaratorInfo.complete(ThisDecl); + // Set the Decl for any late parsed attributes + for (unsigned i = 0, ni = LateParsedAttrs.size(); i < ni; ++i) { + LateParsedAttrs[i]->setDecl(ThisDecl); + } + LateParsedAttrs.clear(); + if (HasDeferredInitializer) { if (!getLang().CPlusPlus0x) Diag(Tok, diag::warn_nonstatic_member_init_accepted_as_extension); @@ -2153,8 +2168,10 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, if (TagDecl && NonNestedClass) { // We are not inside a nested class. This class and its nested classes // are complete and we can parse the delayed portions of method - // declarations and the lexed inline method definitions. + // declarations and the lexed inline method definitions, along with any + // delayed attributes. SourceLocation SavedPrevTokLocation = PrevTokLocation; + ParseLexedAttributes(getCurrentClass()); ParseLexedMethodDeclarations(getCurrentClass()); ParseLexedMemberInitializers(getCurrentClass()); ParseLexedMethodDefs(getCurrentClass()); diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index b11089cc1b..2d6a483e0d 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -734,6 +734,8 @@ class LockID { NamedDecl *ND = ME->getMemberDecl(); DeclSeq.push_back(ND); buildLock(ME->getBase()); + } else if (isa<CXXThisExpr>(Exp)) { + return; } else { // FIXME: add diagnostic llvm::report_fatal_error("Expected lock expression!"); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index f4898a1dd8..251913eb79 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -6778,6 +6778,15 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, return dcl; } + +/// When we finish delayed parsing of an attribute, we must attach it to the +/// relevant Decl. +void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D, + ParsedAttributes &Attrs) { + ProcessDeclAttributeList(S, D, Attrs.getList()); +} + + /// ImplicitlyDefineFunction - An undeclared identifier was used in a function /// call, forming a call to an implicitly defined function (per C99 6.5.1p2). NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, diff --git a/test/SemaCXX/warn-thread-safety-analysis.cpp b/test/SemaCXX/warn-thread-safety-analysis.cpp index cbc947787d..6483a90a3c 100644 --- a/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -407,3 +407,28 @@ void gb_bad_9() { expected-warning {{accessing variable 'sls_guard_var' requires some lock}} } +//-----------------------------------------------// +// Warnings on variables with late parsed attributes +// ----------------------------------------------// + +class LateFoo { +public: + int a __attribute__((guarded_by(mu))); + int b; + + void foo() __attribute__((exclusive_locks_required(mu))) { } + + void test() { + a = 0; // \ + expected-warning {{accessing variable 'a' requires lock 'mu'}} + b = a; // \ + expected-warning {{accessing variable 'a' requires lock 'mu'}} + c = 0; // \ + expected-warning {{accessing variable 'c' requires lock 'mu'}} + } + + int c __attribute__((guarded_by(mu))); + + Mutex mu; +}; + diff --git a/test/SemaCXX/warn-thread-safety-parsing.cpp b/test/SemaCXX/warn-thread-safety-parsing.cpp index e943f3b383..5063c643c3 100644 --- a/test/SemaCXX/warn-thread-safety-parsing.cpp +++ b/test/SemaCXX/warn-thread-safety-parsing.cpp @@ -6,6 +6,8 @@ //-----------------------------------------// class __attribute__((lockable)) Mu { + public: + void Lock(); }; class UnlockableMu{ @@ -40,6 +42,26 @@ Mu* muPointer; Mu ** muDoublePointer = & muPointer; Mu& muRef = mu1; +//---------------------------------------// +// Scoping tests +//--------------------------------------// + +class Foo { + Mu foomu; + void needLock() __attribute__((exclusive_lock_function(foomu))); +}; + +class Foo2 { + void needLock() __attribute__((exclusive_lock_function(foomu))); + Mu foomu; +}; + +class Bar { + Mu barmu; + Mu barmu2 __attribute__((acquired_after(barmu))); +}; + + //-----------------------------------------// // No Thread Safety Analysis (noanal) // //-----------------------------------------// |