aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaitlin Sadowski <supertri@google.com>2011-09-08 17:42:22 +0000
committerCaitlin Sadowski <supertri@google.com>2011-09-08 17:42:22 +0000
commiteff98fc3561f6b717f6348f04b3f4fe03e934466 (patch)
treeafe927f3c46c971b5e38f83f21da5b6fd6b76bd5
parentaebb653a28b142b570596e45cb31b1d5fce1710c (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.td17
-rw-r--r--include/clang/CMakeLists.txt1
-rw-r--r--include/clang/Makefile2
-rw-r--r--include/clang/Parse/CMakeLists.txt4
-rw-r--r--include/clang/Parse/Makefile13
-rw-r--r--include/clang/Parse/Parser.h48
-rw-r--r--include/clang/Sema/Sema.h4
-rw-r--r--lib/Parse/CMakeLists.txt2
-rw-r--r--lib/Parse/ParseDecl.cpp400
-rw-r--r--lib/Parse/ParseDeclCXX.cpp25
-rw-r--r--lib/Sema/AnalysisBasedWarnings.cpp2
-rw-r--r--lib/Sema/SemaDecl.cpp9
-rw-r--r--test/SemaCXX/warn-thread-safety-analysis.cpp25
-rw-r--r--test/SemaCXX/warn-thread-safety-parsing.cpp22
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) //
//-----------------------------------------//