From 284b3cbfd1cd5b6585437fbb8b95fe136f273efb Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sun, 12 May 2013 17:32:42 +0000 Subject: C++1y: support for 'switch' statements in constexpr functions. This is somewhat inefficient; we perform a linear scan of switch labels to find the one matching the condition, and then walk the body looking for that label. Both parts should be straightforward to optimize. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181671 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Stmt.h | 3 - lib/AST/ExprConstant.cpp | 151 +++++++++++++++++++++++++++-- test/SemaCXX/constant-expression-cxx1y.cpp | 114 ++++++++++++++++++++++ 3 files changed, 256 insertions(+), 12 deletions(-) diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 74c9ec2053..b2ab672627 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -967,9 +967,6 @@ public: SwitchCase *getSwitchCaseList() { return FirstCase; } /// \brief Set the case list for this switch statement. - /// - /// The caller is responsible for incrementing the retain counts on - /// all of the SwitchCase statements in this list. void setSwitchCaseList(SwitchCase *SC) { FirstCase = SC; } SourceLocation getSwitchLoc() const { return SwitchLoc; } diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index bf967961aa..339e78bbe9 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -2769,7 +2769,9 @@ enum EvalStmtResult { /// Hit a 'continue' statement. ESR_Continue, /// Hit a 'break' statement. - ESR_Break + ESR_Break, + /// Still scanning for 'case' or 'default' statement. + ESR_CaseNotFound }; } @@ -2803,12 +2805,13 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl, } static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, - const Stmt *S); + const Stmt *S, const SwitchCase *SC = 0); /// Evaluate the body of a loop, and translate the result as appropriate. static EvalStmtResult EvaluateLoopBody(APValue &Result, EvalInfo &Info, - const Stmt *Body) { - switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, Body)) { + const Stmt *Body, + const SwitchCase *Case = 0) { + switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case)) { case ESR_Break: return ESR_Succeeded; case ESR_Succeeded: @@ -2816,17 +2819,128 @@ static EvalStmtResult EvaluateLoopBody(APValue &Result, EvalInfo &Info, return ESR_Continue; case ESR_Failed: case ESR_Returned: + case ESR_CaseNotFound: return ESR; } llvm_unreachable("Invalid EvalStmtResult!"); } +/// Evaluate a switch statement. +static EvalStmtResult EvaluateSwitch(APValue &Result, EvalInfo &Info, + const SwitchStmt *SS) { + // Evaluate the switch condition. + if (SS->getConditionVariable() && + !EvaluateDecl(Info, SS->getConditionVariable())) + return ESR_Failed; + APSInt Value; + if (!EvaluateInteger(SS->getCond(), Value, Info)) + return ESR_Failed; + + // Find the switch case corresponding to the value of the condition. + // FIXME: Cache this lookup. + const SwitchCase *Found = 0; + for (const SwitchCase *SC = SS->getSwitchCaseList(); SC; + SC = SC->getNextSwitchCase()) { + if (isa(SC)) { + Found = SC; + continue; + } + + const CaseStmt *CS = cast(SC); + APSInt LHS = CS->getLHS()->EvaluateKnownConstInt(Info.Ctx); + APSInt RHS = CS->getRHS() ? CS->getRHS()->EvaluateKnownConstInt(Info.Ctx) + : LHS; + if (LHS <= Value && Value <= RHS) { + Found = SC; + break; + } + } + + if (!Found) + return ESR_Succeeded; + + // Search the switch body for the switch case and evaluate it from there. + switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found)) { + case ESR_Break: + return ESR_Succeeded; + case ESR_Succeeded: + case ESR_Continue: + case ESR_Failed: + case ESR_Returned: + return ESR; + case ESR_CaseNotFound: + Found->dump(); + SS->getBody()->dump(); + llvm_unreachable("couldn't find switch case"); + } +} + // Evaluate a statement. static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, - const Stmt *S) { + const Stmt *S, const SwitchCase *Case) { if (!Info.nextStep(S)) return ESR_Failed; + // If we're hunting down a 'case' or 'default' label, recurse through + // substatements until we hit the label. + if (Case) { + // FIXME: We don't start the lifetime of objects whose initialization we + // jump over. However, such objects must be of class type with a trivial + // default constructor that initialize all subobjects, so must be empty, + // so this almost never matters. + switch (S->getStmtClass()) { + case Stmt::CompoundStmtClass: + // FIXME: Precompute which substatement of a compound statement we + // would jump to, and go straight there rather than performing a + // linear scan each time. + case Stmt::LabelStmtClass: + case Stmt::AttributedStmtClass: + case Stmt::DoStmtClass: + break; + + case Stmt::CaseStmtClass: + case Stmt::DefaultStmtClass: + if (Case == S) + Case = 0; + break; + + case Stmt::IfStmtClass: { + // FIXME: Precompute which side of an 'if' we would jump to, and go + // straight there rather than scanning both sides. + const IfStmt *IS = cast(S); + EvalStmtResult ESR = EvaluateStmt(Result, Info, IS->getThen(), Case); + if (ESR != ESR_CaseNotFound || !IS->getElse()) + return ESR; + return EvaluateStmt(Result, Info, IS->getElse(), Case); + } + + case Stmt::WhileStmtClass: { + EvalStmtResult ESR = + EvaluateLoopBody(Result, Info, cast(S)->getBody(), Case); + if (ESR != ESR_Continue) + return ESR; + break; + } + + case Stmt::ForStmtClass: { + const ForStmt *FS = cast(S); + EvalStmtResult ESR = + EvaluateLoopBody(Result, Info, FS->getBody(), Case); + if (ESR != ESR_Continue) + return ESR; + if (FS->getInc() && !EvaluateIgnoredValue(Info, FS->getInc())) + return ESR_Failed; + break; + } + + case Stmt::DeclStmtClass: + // FIXME: If the variable has initialization that can't be jumped over, + // bail out of any immediately-surrounding compound-statement too. + default: + return ESR_CaseNotFound; + } + } + // FIXME: Mark all temporaries in the current frame as destroyed at // the end of each full-expression. switch (S->getStmtClass()) { @@ -2865,11 +2979,13 @@ static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, const CompoundStmt *CS = cast(S); for (CompoundStmt::const_body_iterator BI = CS->body_begin(), BE = CS->body_end(); BI != BE; ++BI) { - EvalStmtResult ESR = EvaluateStmt(Result, Info, *BI); - if (ESR != ESR_Succeeded) + EvalStmtResult ESR = EvaluateStmt(Result, Info, *BI, Case); + if (ESR == ESR_Succeeded) + Case = 0; + else if (ESR != ESR_CaseNotFound) return ESR; } - return ESR_Succeeded; + return Case ? ESR_CaseNotFound : ESR_Succeeded; } case Stmt::IfStmtClass: { @@ -2909,9 +3025,10 @@ static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, const DoStmt *DS = cast(S); bool Continue; do { - EvalStmtResult ESR = EvaluateLoopBody(Result, Info, DS->getBody()); + EvalStmtResult ESR = EvaluateLoopBody(Result, Info, DS->getBody(), Case); if (ESR != ESR_Continue) return ESR; + Case = 0; if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info)) return ESR_Failed; @@ -2983,11 +3100,27 @@ static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, return ESR_Succeeded; } + case Stmt::SwitchStmtClass: + return EvaluateSwitch(Result, Info, cast(S)); + case Stmt::ContinueStmtClass: return ESR_Continue; case Stmt::BreakStmtClass: return ESR_Break; + + case Stmt::LabelStmtClass: + return EvaluateStmt(Result, Info, cast(S)->getSubStmt(), Case); + + case Stmt::AttributedStmtClass: + // As a general principle, C++11 attributes can be ignored without + // any semantic impact. + return EvaluateStmt(Result, Info, cast(S)->getSubStmt(), + Case); + + case Stmt::CaseStmtClass: + case Stmt::DefaultStmtClass: + return EvaluateStmt(Result, Info, cast(S)->getSubStmt(), Case); } } diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp index 824a0e8b91..59a972a4fd 100644 --- a/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -593,3 +593,117 @@ namespace assignment_op { } static_assert(testC(), ""); } + +namespace switch_stmt { + constexpr int f(char k) { + bool b = false; + int z = 6; + switch (k) { + return -1; + case 0: + if (false) { + case 1: + z = 1; + for (; b;) { + return 5; + while (0) + case 2: return 2; + case 7: z = 7; + do case 6: { + return z; + if (false) + case 3: return 3; + case 4: z = 4; + } while (1); + case 5: b = true; + case 9: z = 9; + } + return z; + } else if (false) case 8: z = 8; + else if (false) { + case 10: + z = -10; + break; + } + else z = 0; + return z; + default: + return -1; + } + return -z; + } + static_assert(f(0) == 0, ""); + static_assert(f(1) == 1, ""); + static_assert(f(2) == 2, ""); + static_assert(f(3) == 3, ""); + static_assert(f(4) == 4, ""); + static_assert(f(5) == 5, ""); + static_assert(f(6) == 6, ""); + static_assert(f(7) == 7, ""); + static_assert(f(8) == 8, ""); + static_assert(f(9) == 9, ""); + static_assert(f(10) == 10, ""); + + // Check that we can continue an outer loop from within a switch. + constexpr bool contin() { + for (int n = 0; n != 10; ++n) { + switch (n) { + case 0: + ++n; + continue; + case 1: + return false; + case 2: + return true; + } + } + return false; + } + static_assert(contin(), ""); + + constexpr bool switch_into_for() { + int n = 0; + switch (n) { + for (; n == 1; ++n) { + return n == 1; + case 0: ; + } + } + return false; + } + static_assert(switch_into_for(), ""); + + constexpr void duff_copy(char *a, const char *b, int n) { + switch ((n - 1) % 8 + 1) { + for ( ; n; n = (n - 1) & ~7) { + case 8: a[n-8] = b[n-8]; + case 7: a[n-7] = b[n-7]; + case 6: a[n-6] = b[n-6]; + case 5: a[n-5] = b[n-5]; + case 4: a[n-4] = b[n-4]; + case 3: a[n-3] = b[n-3]; + case 2: a[n-2] = b[n-2]; + case 1: a[n-1] = b[n-1]; + } + case 0: ; + } + } + + constexpr bool test_copy(const char *str, int n) { + char buffer[16] = {}; + duff_copy(buffer, str, n); + for (int i = 0; i != sizeof(buffer); ++i) + if (buffer[i] != (i < n ? str[i] : 0)) + return false; + return true; + } + static_assert(test_copy("foo", 0), ""); + static_assert(test_copy("foo", 1), ""); + static_assert(test_copy("foo", 2), ""); + static_assert(test_copy("hello world", 0), ""); + static_assert(test_copy("hello world", 7), ""); + static_assert(test_copy("hello world", 8), ""); + static_assert(test_copy("hello world", 9), ""); + static_assert(test_copy("hello world", 10), ""); + static_assert(test_copy("hello world", 10), ""); +} -- cgit v1.2.3 From 87360f2acd4e2fa706707135b95c64e5e2b63435 Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Sun, 12 May 2013 18:05:52 +0000 Subject: Debug Info: Comment changes in r181393 by request of echristo git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181672 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGDebugInfo.cpp | 11 +++++++++++ lib/CodeGen/CGDebugInfo.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 7d5d260304..13b681edc9 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -2172,8 +2172,19 @@ llvm::DIType CGDebugInfo::CreateMemberType(llvm::DIFile Unit, QualType FType, } llvm::DIDescriptor CGDebugInfo::getDeclarationOrDefinition(const Decl *D) { + // We only need a declaration (not a definition) of the type - so use whatever + // we would otherwise do to get a type for a pointee. (forward declarations in + // limited debug info, full definitions (if the type definition is available) + // in unlimited debug info) if (const TypeDecl *RD = dyn_cast(D)) return CreatePointeeType(QualType(RD->getTypeForDecl(), 0), llvm::DIFile()); + // Otherwise fall back to a fairly rudimentary cache of existing declarations. + // This doesn't handle providing declarations (for functions or variables) for + // entities without definitions in this TU, nor when the definition proceeds + // the call to this function. + // FIXME: This should be split out into more specific maps with support for + // emitting forward declarations and merging definitions with declarations, + // the same way as we do for types. llvm::DenseMap::iterator I = DeclCache.find(D->getCanonicalDecl()); if (I == DeclCache.end()) diff --git a/lib/CodeGen/CGDebugInfo.h b/lib/CodeGen/CGDebugInfo.h index e61a50eeb4..f221a0cd3e 100644 --- a/lib/CodeGen/CGDebugInfo.h +++ b/lib/CodeGen/CGDebugInfo.h @@ -95,6 +95,8 @@ class CGDebugInfo { llvm::DenseMap DIFileCache; llvm::DenseMap SPCache; + /// \brief Cache declarations relevant to DW_TAG_imported_declarations (C++ + /// using declarations) that aren't covered by other more specific caches. llvm::DenseMap DeclCache; llvm::DenseMap NameSpaceCache; llvm::DenseMap StaticDataMemberCache; -- cgit v1.2.3 From d248e586d60a3fe971369089e763d5e8f669b7a9 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sun, 12 May 2013 23:17:59 +0000 Subject: Fix stack overflow in linkage computation when a function with a deduced return type returns a lambda defined within itself. The computation of linkage for the function looked at the linkage of the lambda, and vice versa. This is solved by not checking whether an 'auto' in a function return type deduces to a type with unique external linkage. We don't need this check, because the type deduced for 'auto' doesn't affect whether two otherwise-identical declarations would name different functions, so we don't need to give an ostensibly external-linkage function internal linkage for this reason. (We also don't need unique-external linkage in C++11 onwards at all, but that's not implemented yet.) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181675 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/Decl.cpp | 17 +++++++++++--- test/CodeGenCXX/cxx1y-deduced-return-type.cpp | 32 +++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 test/CodeGenCXX/cxx1y-deduced-return-type.cpp diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index ab9d73b917..df923d6736 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -660,9 +660,20 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, // this translation unit. However, we should use the C linkage // rules instead for extern "C" declarations. if (Context.getLangOpts().CPlusPlus && - !Function->isInExternCContext() && - Function->getType()->getLinkage() == UniqueExternalLinkage) - return LinkageInfo::uniqueExternal(); + !Function->isInExternCContext()) { + // Only look at the type-as-written. If this function has an auto-deduced + // return type, we can't compute the linkage of that type because it could + // require looking at the linkage of this function, and we don't need this + // for correctness because the type is not part of the function's + // signature. + // FIXME: This is a hack. We should be able to solve this circularity some + // other way. + QualType TypeAsWritten = Function->getType(); + if (TypeSourceInfo *TSI = Function->getTypeSourceInfo()) + TypeAsWritten = TSI->getType(); + if (TypeAsWritten->getLinkage() == UniqueExternalLinkage) + return LinkageInfo::uniqueExternal(); + } // Consider LV from the template and the template arguments. // We're at file scope, so we do not need to worry about nested diff --git a/test/CodeGenCXX/cxx1y-deduced-return-type.cpp b/test/CodeGenCXX/cxx1y-deduced-return-type.cpp new file mode 100644 index 0000000000..edf673df4a --- /dev/null +++ b/test/CodeGenCXX/cxx1y-deduced-return-type.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -std=c++1y -emit-llvm %s -o - | FileCheck %s + +// CHECK: @x = global {{.*}} zeroinitializer + +// CHECK: define {{.*}} @_Z1fv +inline auto f() { + int n = 0; + // CHECK: load i32 + // CHECK: store i32 + // CHECK: ret + return [=] () mutable { return ++n; }; +} + +auto x = f(); + +template auto *g(T t) { return t; } +template decltype(auto) h(T t) { return t; } + +// CHECK: define {{.*}} @_Z1zv +void z() { + // CHECK: call {{.*}} @_Z1gIPZ1fvEUlvE_EPDaT_( + // CHECK: call {{.*}} @_Z1hIPZ1fvEUlvE_EDcT_( + g(&x); + h(&x); +} + +auto i() { return [] {}; } +// CHECK: define {{.*}} @_Z1jv +auto j() { + // CHECK: call {{.*}} @"_Z1hIZ1ivE3$_0EDcT_"() + h(i()); +} -- cgit v1.2.3 From 3c5f4b649f7a8ed51f9291c494a1b6e43f69d386 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sun, 12 May 2013 23:39:32 +0000 Subject: Downgrade C++14 "Clarifying memory allocation". We perform non-conforming optimizations -- in particular, globalopt will remove calls to ::operator new(size_t) that did not come from new-expressions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181676 91177308-0d34-0410-b5e6-96231b3b80d8 --- www/cxx_status.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/cxx_status.html b/www/cxx_status.html index 7643c7a7b1..a0c697f149 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -467,7 +467,7 @@ available.

Clarifying memory allocation N3664 - Yes + Partial -- cgit v1.2.3 From 181e3ecc0907ae0103586a9f4db52241995a8267 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Mon, 13 May 2013 00:12:11 +0000 Subject: Cleanup handling of UniqueExternalLinkage. This patch renames getLinkage to getLinkageInternal. Only code that needs to handle UniqueExternalLinkage specially should call this. Linkage, as defined in the c++ standard, is provided by getFormalLinkage. It maps UniqueExternalLinkage to ExternalLinkage. Most places in the compiler actually want isExternallyVisible, which handles UniqueExternalLinkage as internal. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181677 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 21 +++++++-- include/clang/Basic/Linkage.h | 5 -- include/clang/Sema/Sema.h | 2 +- lib/ARCMigrate/TransUnbridgedCasts.cpp | 4 +- lib/ARCMigrate/Transforms.cpp | 4 +- lib/AST/ASTContext.cpp | 75 ++++++++++++------------------ lib/AST/ASTImporter.cpp | 8 ++-- lib/AST/Decl.cpp | 10 ++-- lib/AST/ItaniumMangle.cpp | 4 +- lib/AST/MicrosoftMangle.cpp | 2 +- lib/AST/RecordLayoutBuilder.cpp | 2 +- lib/AST/Type.cpp | 4 +- lib/CodeGen/CGVTables.cpp | 2 +- lib/CodeGen/CodeGenModule.cpp | 2 +- lib/CodeGen/CodeGenTBAA.cpp | 3 +- lib/Sema/Sema.cpp | 8 ++-- lib/Sema/SemaDecl.cpp | 22 ++++----- lib/Sema/SemaDeclCXX.cpp | 2 +- lib/Sema/SemaExpr.cpp | 8 ++-- lib/Sema/SemaTemplate.cpp | 4 +- lib/Serialization/ASTReaderDecl.cpp | 4 +- lib/Serialization/ASTWriterDecl.cpp | 4 +- lib/StaticAnalyzer/Core/CheckerContext.cpp | 2 +- tools/libclang/CIndex.cpp | 2 +- tools/libclang/CIndexUSRs.cpp | 2 +- tools/libclang/IndexingContext.cpp | 3 +- 26 files changed, 104 insertions(+), 105 deletions(-) diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index d5e6ebee4a..e35938a5e0 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -212,11 +212,26 @@ public: bool isCXXInstanceMember() const; /// \brief Determine what kind of linkage this entity has. - Linkage getLinkage() const; + /// This is not the linkage as defined by the standard or the codegen notion + /// of linkage. It is just an implementation detail that is used to compute + /// those. + Linkage getLinkageInternal() const; + + /// \brief Get the linkage from a semantic point of view. Entities in + /// anonymous namespaces are external (in c++98). + Linkage getFormalLinkage() const { + Linkage L = getLinkageInternal(); + if (L == UniqueExternalLinkage) + return ExternalLinkage; + return L; + } /// \brief True if this decl has external linkage. - bool hasExternalLinkage() const { - return getLinkage() == ExternalLinkage; + bool hasExternalFormalLinkage() const { + return getFormalLinkage() == ExternalLinkage; + } + bool isExternallyVisible() const { + return getLinkageInternal() == ExternalLinkage; } /// \brief Determines the visibility of this entity. diff --git a/include/clang/Basic/Linkage.h b/include/clang/Basic/Linkage.h index 01b8db15f5..13d2d24d8e 100644 --- a/include/clang/Basic/Linkage.h +++ b/include/clang/Basic/Linkage.h @@ -62,11 +62,6 @@ enum GVALinkage { GVA_ExplicitTemplateInstantiation }; -/// \brief Determine whether the given linkage is semantically external. -inline bool isExternalLinkage(Linkage L) { - return L == UniqueExternalLinkage || L == ExternalLinkage; -} - /// \brief Compute the minimum linkage given two linages. inline Linkage minLinkage(Linkage L1, Linkage L2) { return L1 < L2? L1 : L2; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 8e1ebd844e..78152313c8 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -216,7 +216,7 @@ class Sema { // it will keep having external linkage. If it has internal linkage, we // will not link it. Since it has no previous decls, it will remain // with internal linkage. - return !Old->isHidden() || New->hasExternalLinkage(); + return !Old->isHidden() || New->isExternallyVisible(); } public: diff --git a/lib/ARCMigrate/TransUnbridgedCasts.cpp b/lib/ARCMigrate/TransUnbridgedCasts.cpp index fc4a75fdb8..a5752f8157 100644 --- a/lib/ARCMigrate/TransUnbridgedCasts.cpp +++ b/lib/ARCMigrate/TransUnbridgedCasts.cpp @@ -148,7 +148,7 @@ private: if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 && FD->getParent()->isTranslationUnit() && - FD->hasExternalLinkage()) { + FD->isExternallyVisible()) { Expr *Arg = callE->getArg(0); if (const ImplicitCastExpr *ICE = dyn_cast(Arg)) { const Expr *sub = ICE->getSubExpr(); @@ -413,7 +413,7 @@ private: FD = dyn_cast_or_null(callE->getCalleeDecl())) if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 && FD->getParent()->isTranslationUnit() && - FD->hasExternalLinkage()) + FD->isExternallyVisible()) return true; return false; diff --git a/lib/ARCMigrate/Transforms.cpp b/lib/ARCMigrate/Transforms.cpp index 087219535a..6c724af53a 100644 --- a/lib/ARCMigrate/Transforms.cpp +++ b/lib/ARCMigrate/Transforms.cpp @@ -94,7 +94,7 @@ bool trans::isPlusOne(const Expr *E) { if (FD->isGlobal() && FD->getIdentifier() && FD->getParent()->isTranslationUnit() && - FD->hasExternalLinkage() && + FD->isExternallyVisible() && ento::cocoa::isRefType(callE->getType(), "CF", FD->getIdentifier()->getName())) { StringRef fname = FD->getIdentifier()->getName(); @@ -198,7 +198,7 @@ bool trans::isGlobalVar(Expr *E) { E = E->IgnoreParenCasts(); if (DeclRefExpr *DRE = dyn_cast(E)) return DRE->getDecl()->getDeclContext()->isFileContext() && - DRE->getDecl()->hasExternalLinkage(); + DRE->getDecl()->isExternallyVisible(); if (ConditionalOperator *condOp = dyn_cast(E)) return isGlobalVar(condOp->getTrueExpr()) && isGlobalVar(condOp->getFalseExpr()); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 986bb8c3d2..9901080101 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -7767,30 +7767,23 @@ QualType ASTContext::GetBuiltinType(unsigned Id, } GVALinkage ASTContext::GetGVALinkageForFunction(const FunctionDecl *FD) { - GVALinkage External = GVA_StrongExternal; - - Linkage L = FD->getLinkage(); - switch (L) { - case NoLinkage: - case InternalLinkage: - case UniqueExternalLinkage: + if (!FD->isExternallyVisible()) return GVA_Internal; - - case ExternalLinkage: - switch (FD->getTemplateSpecializationKind()) { - case TSK_Undeclared: - case TSK_ExplicitSpecialization: - External = GVA_StrongExternal; - break; - case TSK_ExplicitInstantiationDefinition: - return GVA_ExplicitTemplateInstantiation; + GVALinkage External = GVA_StrongExternal; + switch (FD->getTemplateSpecializationKind()) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + External = GVA_StrongExternal; + break; - case TSK_ExplicitInstantiationDeclaration: - case TSK_ImplicitInstantiation: - External = GVA_TemplateInstantiation; - break; - } + case TSK_ExplicitInstantiationDefinition: + return GVA_ExplicitTemplateInstantiation; + + case TSK_ExplicitInstantiationDeclaration: + case TSK_ImplicitInstantiation: + External = GVA_TemplateInstantiation; + break; } if (!FD->isInlined()) @@ -7820,6 +7813,9 @@ GVALinkage ASTContext::GetGVALinkageForFunction(const FunctionDecl *FD) { } GVALinkage ASTContext::GetGVALinkageForVariable(const VarDecl *VD) { + if (!VD->isExternallyVisible()) + return GVA_Internal; + // If this is a static data member, compute the kind of template // specialization. Otherwise, this variable is not part of a // template. @@ -7827,33 +7823,21 @@ GVALinkage ASTContext::GetGVALinkageForVariable(const VarDecl *VD) { if (VD->isStaticDataMember()) TSK = VD->getTemplateSpecializationKind(); - Linkage L = VD->getLinkage(); - - switch (L) { - case NoLinkage: - case InternalLinkage: - case UniqueExternalLinkage: - return GVA_Internal; + switch (TSK) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + return GVA_StrongExternal; - case ExternalLinkage: - switch (TSK) { - case TSK_Undeclared: - case TSK_ExplicitSpecialization: - return GVA_StrongExternal; + case TSK_ExplicitInstantiationDeclaration: + llvm_unreachable("Variable should not be instantiated"); + // Fall through to treat this like any other instantiation. - case TSK_ExplicitInstantiationDeclaration: - llvm_unreachable("Variable should not be instantiated"); - // Fall through to treat this like any other instantiation. - - case TSK_ExplicitInstantiationDefinition: - return GVA_ExplicitTemplateInstantiation; + case TSK_ExplicitInstantiationDefinition: + return GVA_ExplicitTemplateInstantiation; - case TSK_ImplicitInstantiation: - return GVA_TemplateInstantiation; - } + case TSK_ImplicitInstantiation: + return GVA_TemplateInstantiation; } - - llvm_unreachable("Invalid Linkage!"); } bool ASTContext::DeclMustBeEmitted(const Decl *D) { @@ -7986,7 +7970,8 @@ size_t ASTContext::getSideTableAllocatedMemory() const { void ASTContext::addUnnamedTag(const TagDecl *Tag) { // FIXME: This mangling should be applied to function local classes too if (!Tag->getName().empty() || Tag->getTypedefNameForAnonDecl() || - !isa(Tag->getParent()) || Tag->getLinkage() != ExternalLinkage) + !isa(Tag->getParent()) || + !Tag->isExternallyVisible()) return; std::pair::iterator, bool> P = diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 915eb6feb8..740b4ff721 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -2610,8 +2610,8 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) { continue; if (FunctionDecl *FoundFunction = dyn_cast(FoundDecls[I])) { - if (isExternalLinkage(FoundFunction->getLinkage()) && - isExternalLinkage(D->getLinkage())) { + if (FoundFunction->hasExternalFormalLinkage() && + D->hasExternalFormalLinkage()) { if (Importer.IsStructurallyEquivalent(D->getType(), FoundFunction->getType())) { // FIXME: Actually try to merge the body and other attributes. @@ -2995,8 +2995,8 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) { if (VarDecl *FoundVar = dyn_cast(FoundDecls[I])) { // We have found a variable that we may need to merge with. Check it. - if (isExternalLinkage(FoundVar->getLinkage()) && - isExternalLinkage(D->getLinkage())) { + if (FoundVar->hasExternalFormalLinkage() && + D->hasExternalFormalLinkage()) { if (Importer.IsStructurallyEquivalent(D->getType(), FoundVar->getType())) { MergeWithVar = FoundVar; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index df923d6736..3e4c2cbdb2 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -483,6 +483,10 @@ static bool isSingleLineExternC(const Decl &D) { return false; } +static bool isExternalLinkage(Linkage L) { + return L == UniqueExternalLinkage || L == ExternalLinkage; +} + static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVComputationKind computation) { assert(D->getDeclContext()->getRedeclContext()->isFileContext() && @@ -885,7 +889,7 @@ bool NamedDecl::isLinkageValid() const { Linkage(CachedLinkage); } -Linkage NamedDecl::getLinkage() const { +Linkage NamedDecl::getLinkageInternal() const { if (HasCachedLinkage) return Linkage(CachedLinkage); @@ -1289,7 +1293,7 @@ bool NamedDecl::declarationReplaces(NamedDecl *OldD) const { } bool NamedDecl::hasLinkage() const { - return getLinkage() != NoLinkage; + return getLinkageInternal() != NoLinkage; } NamedDecl *NamedDecl::getUnderlyingDeclImpl() { @@ -1513,7 +1517,7 @@ template static LanguageLinkage getLanguageLinkageTemplate(const T &D) { // C++ [dcl.link]p1: All function types, function names with external linkage, // and variable names with external linkage have a language linkage. - if (!isExternalLinkage(D.getLinkage())) + if (!D.hasExternalFormalLinkage()) return NoLanguageLinkage; // Language linkage is a C++ concept, but saying that everything else in C has diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index d28aced172..3137f3c8fd 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -405,7 +405,7 @@ bool ItaniumMangleContext::shouldMangleDeclName(const NamedDecl *D) { if (DC->isFunctionOrMethod() && D->hasLinkage()) while (!DC->isNamespace() && !DC->isTranslationUnit()) DC = getEffectiveParentContext(DC); - if (DC->isTranslationUnit() && D->getLinkage() != InternalLinkage) + if (DC->isTranslationUnit() && D->getFormalLinkage() != InternalLinkage) return false; } @@ -1053,7 +1053,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, // static void foo(); // This naming convention is the same as that followed by GCC, // though it shouldn't actually matter. - if (ND && ND->getLinkage() == InternalLinkage && + if (ND && ND->getFormalLinkage() == InternalLinkage && getEffectiveDeclContext(ND)->isFileContext()) Out << 'L'; diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 7c797eef4d..d242cd9335 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -199,7 +199,7 @@ bool MicrosoftMangleContext::shouldMangleDeclName(const NamedDecl *D) { // Variables at global scope with internal linkage are not mangled. if (!FD) { const DeclContext *DC = D->getDeclContext(); - if (DC->isTranslationUnit() && D->getLinkage() == InternalLinkage) + if (DC->isTranslationUnit() && D->getFormalLinkage() == InternalLinkage) return false; } diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index 42c3ba31bc..f27b502c9c 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -2354,7 +2354,7 @@ static const CXXMethodDecl *computeKeyFunction(ASTContext &Context, // A class that is not externally visible doesn't have a key function. (Or // at least, there's no point to assigning a key function to such a class; // this doesn't affect the ABI.) - if (RD->getLinkage() != ExternalLinkage) + if (!RD->isExternallyVisible()) return 0; // Template instantiations don't have key functions,see Itanium C++ ABI 5.2.6. diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index a1f0b08494..eacf367818 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2124,7 +2124,7 @@ static CachedProperties computeCachedProperties(const Type *T) { // - it is a class or enumeration type that is named (or has a name // for linkage purposes (7.1.3)) and the name has linkage; or // - it is a specialization of a class template (14); or - Linkage L = Tag->getLinkage(); + Linkage L = Tag->getLinkageInternal(); bool IsLocalOrUnnamed = Tag->getDeclContext()->isFunctionOrMethod() || !Tag->hasNameForLinkage(); @@ -2166,7 +2166,7 @@ static CachedProperties computeCachedProperties(const Type *T) { return result; } case Type::ObjCInterface: { - Linkage L = cast(T)->getDecl()->getLinkage(); + Linkage L = cast(T)->getDecl()->getLinkageInternal(); return CachedProperties(L, false); } case Type::ObjCObject: diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index 069cd5f9e7..9a345cf472 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -719,7 +719,7 @@ CodeGenVTables::GenerateConstructionVTable(const CXXRecordDecl *RD, /// Note that we only call this at the end of the translation unit. llvm::GlobalVariable::LinkageTypes CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) { - if (RD->getLinkage() != ExternalLinkage) + if (!RD->isExternallyVisible()) return llvm::GlobalVariable::InternalLinkage; // We're at the end of the translation unit, so the current key diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 695048e5b3..0736879037 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -1763,7 +1763,7 @@ void CodeGenModule::MaybeHandleStaticInExternC(const SomeDecl *D, return; // Must have internal linkage and an ordinary name. - if (!D->getIdentifier() || D->getLinkage() != InternalLinkage) + if (!D->getIdentifier() || D->getFormalLinkage() != InternalLinkage) return; // Must be in an extern "C" context. Entities declared directly within diff --git a/lib/CodeGen/CodeGenTBAA.cpp b/lib/CodeGen/CodeGenTBAA.cpp index 5ff1560a48..3f0eaee536 100644 --- a/lib/CodeGen/CodeGenTBAA.cpp +++ b/lib/CodeGen/CodeGenTBAA.cpp @@ -162,8 +162,7 @@ CodeGenTBAA::getTBAAInfo(QualType QTy) { // on their mangled names, if they're external. // TODO: Is there a way to get a program-wide unique name for a // decl with local linkage or no linkage? - if (Features.CPlusPlus && - ETy->getDecl()->getLinkage() != ExternalLinkage) + if (Features.CPlusPlus && !ETy->getDecl()->isExternallyVisible()) return MetadataCache[Ty] = getChar(); // TODO: This is using the RTTI name. Is there a better way to get diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index e718be2f8b..b7810da98e 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -332,7 +332,7 @@ static bool ShouldRemoveFromUnused(Sema *SemaRef, const DeclaratorDecl *D) { if (D->getMostRecentDecl()->isUsed()) return true; - if (D->hasExternalLinkage()) + if (D->isExternallyVisible()) return true; if (const FunctionDecl *FD = dyn_cast(D)) { @@ -402,13 +402,13 @@ void Sema::getUndefinedButUsed( if (FunctionDecl *FD = dyn_cast(ND)) { if (FD->isDefined()) continue; - if (FD->hasExternalLinkage() && + if (FD->isExternallyVisible() && !FD->getMostRecentDecl()->isInlined()) continue; } else { if (cast(ND)->hasDefinition() != VarDecl::DeclarationOnly) continue; - if (ND->hasExternalLinkage()) + if (ND->isExternallyVisible()) continue; } @@ -435,7 +435,7 @@ static void checkUndefinedButUsed(Sema &S) { I = Undefined.begin(), E = Undefined.end(); I != E; ++I) { NamedDecl *ND = I->first; - if (ND->getLinkage() != ExternalLinkage) { + if (!ND->isExternallyVisible()) { S.Diag(ND->getLocation(), diag::warn_undefined_internal) << isa(ND) << ND; } else { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 5d8839a65b..49be515f0b 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1194,7 +1194,7 @@ bool Sema::mightHaveNonExternalLinkage(const DeclaratorDecl *D) { DC = DC->getParent(); } - return !D->hasExternalLinkage(); + return !D->isExternallyVisible(); } bool Sema::ShouldWarnIfUnusedFileScopedDecl(const DeclaratorDecl *D) const { @@ -1614,7 +1614,7 @@ static void filterNonConflictingPreviousDecls(ASTContext &context, if (!old->isHidden()) continue; - if (old->getLinkage() != ExternalLinkage) + if (!old->isExternallyVisible()) filter.erase(); } @@ -2314,7 +2314,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD, Scope *S) { // storage classes. if (!isa(New) && !isa(Old) && New->getStorageClass() == SC_Static && - isExternalLinkage(Old->getLinkage()) && + Old->hasExternalFormalLinkage() && !New->getTemplateSpecializationInfo() && !canRedefineFunction(Old, getLangOpts())) { if (getLangOpts().MicrosoftExt) { @@ -2923,7 +2923,7 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous, // [dcl.stc]p8: Check if we have a non-static decl followed by a static. if (New->getStorageClass() == SC_Static && !New->isStaticDataMember() && - isExternalLinkage(Old->getLinkage())) { + Old->hasExternalFormalLinkage()) { Diag(New->getLocation(), diag::err_static_non_static) << New->getDeclName(); Diag(Old->getLocation(), diag::note_previous_definition); return New->setInvalidDecl(); @@ -4625,13 +4625,13 @@ bool Sema::inferObjCARCLifetime(ValueDecl *decl) { static void checkAttributesAfterMerging(Sema &S, NamedDecl &ND) { // 'weak' only applies to declarations with external linkage. if (WeakAttr *Attr = ND.getAttr()) { - if (ND.getLinkage() != ExternalLinkage) { + if (!ND.isExternallyVisible()) { S.Diag(Attr->getLocation(), diag::err_attribute_weak_static); ND.dropAttr(); } } if (WeakRefAttr *Attr = ND.getAttr()) { - if (ND.hasExternalLinkage()) { + if (ND.isExternallyVisible()) { S.Diag(Attr->getLocation(), diag::err_attribute_weakref_not_static); ND.dropAttr(); } @@ -6579,7 +6579,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // If there's a #pragma GCC visibility in scope, and this isn't a class // member, set the visibility of this function. - if (!DC->isRecord() && NewFD->hasExternalLinkage()) + if (!DC->isRecord() && NewFD->isExternallyVisible()) AddPushedVisibilityAttribute(NewFD); // If there's a #pragma clang arc_cf_code_audited in scope, consider @@ -7816,7 +7816,7 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl, // declared with no linkage (C99 6.2.2p6), the type for the // object shall be complete. if (!Type->isDependentType() && Var->isLocalVarDecl() && - !Var->getLinkage() && !Var->isInvalidDecl() && + !Var->hasLinkage() && !Var->isInvalidDecl() && RequireCompleteType(Var->getLocation(), Type, diag::err_typecheck_decl_incomplete_type)) Var->setInvalidDecl(); @@ -8029,7 +8029,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { } if (var->isThisDeclarationADefinition() && - var->hasExternalLinkage() && + var->isExternallyVisible() && getDiagnostics().getDiagnosticLevel( diag::warn_missing_variable_declarations, var->getLocation())) { @@ -8138,7 +8138,7 @@ Sema::FinalizeDeclaration(Decl *ThisDecl) { const DeclContext *DC = VD->getDeclContext(); // If there's a #pragma GCC visibility in scope, and this isn't a class // member, set the visibility of this variable. - if (!DC->isRecord() && VD->hasExternalLinkage()) + if (!DC->isRecord() && VD->isExternallyVisible()) AddPushedVisibilityAttribute(VD); if (VD->isFileVarDecl()) @@ -8904,7 +8904,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // ODR use before the definition. Avoid the expensive map lookup if this // is the first declaration. if (FD->getPreviousDecl() != 0 && FD->getPreviousDecl()->isUsed()) { - if (FD->getLinkage() != ExternalLinkage) + if (!FD->isExternallyVisible()) UndefinedButUsed.erase(FD); else if (FD->isInlined() && (LangOpts.CPlusPlus || !LangOpts.GNUInline) && diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index e5d0316f73..e5e43897ee 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -11824,7 +11824,7 @@ bool Sema::DefineUsedVTables() { Consumer.HandleVTable(Class, VTablesUsed[Canonical]); // Optionally warn if we're emitting a weak vtable. - if (Class->hasExternalLinkage() && + if (Class->isExternallyVisible() && Class->getTemplateSpecializationKind() != TSK_ImplicitInstantiation) { const FunctionDecl *KeyFunctionDef = 0; if (!KeyFunction || diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index b6b6444738..2595f2d099 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -197,11 +197,11 @@ static void diagnoseUseOfInternalDeclInInlineFunction(Sema &S, return; if (!Current->isInlined()) return; - if (Current->getLinkage() != ExternalLinkage) + if (!Current->isExternallyVisible()) return; - + // Check if the decl has internal linkage. - if (D->getLinkage() != InternalLinkage) + if (D->getFormalLinkage() != InternalLinkage) return; // Downgrade from ExtWarn to Extension if @@ -11459,7 +11459,7 @@ static void MarkVarDeclODRUsed(Sema &SemaRef, VarDecl *Var, // Keep track of used but undefined variables. // FIXME: We shouldn't suppress this warning for static data members. if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly && - Var->getLinkage() != ExternalLinkage && + !Var->isExternallyVisible() && !(Var->isStaticDataMember() && Var->hasInit())) { SourceLocation &old = SemaRef.UndefinedButUsed[Var->getCanonicalDecl()]; if (old.isInvalid()) old = Loc; diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index b9695cc1e1..7f1def7daf 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -3793,14 +3793,14 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, } // Address / reference template args must have external linkage in C++98. - if (Entity->getLinkage() == InternalLinkage) { + if (Entity->getFormalLinkage() == InternalLinkage) { S.Diag(Arg->getLocStart(), S.getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_template_arg_object_internal : diag::ext_template_arg_object_internal) << !Func << Entity << Arg->getSourceRange(); S.Diag(Entity->getLocation(), diag::note_template_arg_internal_object) << !Func; - } else if (Entity->getLinkage() == NoLinkage) { + } else if (!Entity->hasLinkage()) { S.Diag(Arg->getLocStart(), diag::err_template_arg_object_no_linkage) << !Func << Entity << Arg->getSourceRange(); S.Diag(Entity->getLocation(), diag::note_template_arg_internal_object) diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 086acd261d..e3f9f43a3b 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1783,14 +1783,14 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { //prototyped/non-prototyped functions, etc. if (FunctionDecl *FuncX = dyn_cast(X)) { FunctionDecl *FuncY = cast(Y); - return (FuncX->getLinkage() == FuncY->getLinkage()) && + return (FuncX->getLinkageInternal() == FuncY->getLinkageInternal()) && FuncX->getASTContext().hasSameType(FuncX->getType(), FuncY->getType()); } // Variables with the same type and linkage match. if (VarDecl *VarX = dyn_cast(X)) { VarDecl *VarY = cast(Y); - return (VarX->getLinkage() == VarY->getLinkage()) && + return (VarX->getLinkageInternal() == VarY->getLinkageInternal()) && VarX->getASTContext().hasSameType(VarX->getType(), VarY->getType()); } diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 67349db687..7a22afaba7 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -334,7 +334,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { Record.push_back(D->hasImplicitReturnZero()); Record.push_back(D->isConstexpr()); Record.push_back(D->HasSkippedBody); - Record.push_back(D->getLinkage()); + Record.push_back(D->getLinkageInternal()); Writer.AddSourceLocation(D->getLocEnd(), Record); Record.push_back(D->getTemplatedKind()); @@ -694,7 +694,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { Record.push_back(D->isCXXForRangeDecl()); Record.push_back(D->isARCPseudoStrong()); Record.push_back(D->isConstexpr()); - Record.push_back(D->getLinkage()); + Record.push_back(D->getLinkageInternal()); if (D->getInit()) { Record.push_back(!D->isInitKnownICE() ? 1 : (D->isInitICE() ? 3 : 2)); diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp index 74eeef1c67..6b22bf411c 100644 --- a/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -68,7 +68,7 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, // If this function is not externally visible, it is not a C library function. // Note that we make an exception for inline functions, which may be // declared in header files without external linkage. - if (!FD->isInlined() && FD->getLinkage() != ExternalLinkage) + if (!FD->isInlined() && !FD->isExternallyVisible()) return false; if (Name.empty()) diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index c25e625df5..71827f8a23 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -5681,7 +5681,7 @@ CXLinkageKind clang_getCursorLinkage(CXCursor cursor) { const Decl *D = cxcursor::getCursorDecl(cursor); if (const NamedDecl *ND = dyn_cast_or_null(D)) - switch (ND->getLinkage()) { + switch (ND->getLinkageInternal()) { case NoLinkage: return CXLinkage_NoLinkage; case InternalLinkage: return CXLinkage_Internal; case UniqueExternalLinkage: return CXLinkage_UniqueExternal; diff --git a/tools/libclang/CIndexUSRs.cpp b/tools/libclang/CIndexUSRs.cpp index a911ce5e95..96c1d2e11d 100644 --- a/tools/libclang/CIndexUSRs.cpp +++ b/tools/libclang/CIndexUSRs.cpp @@ -151,7 +151,7 @@ bool USRGenerator::EmitDeclName(const NamedDecl *D) { } static inline bool ShouldGenerateLocation(const NamedDecl *D) { - return D->getLinkage() != ExternalLinkage; + return !D->isExternallyVisible(); } void USRGenerator::VisitDeclContext(const DeclContext *DC) { diff --git a/tools/libclang/IndexingContext.cpp b/tools/libclang/IndexingContext.cpp index 14b430c7dc..cb9b492586 100644 --- a/tools/libclang/IndexingContext.cpp +++ b/tools/libclang/IndexingContext.cpp @@ -210,11 +210,12 @@ bool IndexingContext::isFunctionLocalDecl(const Decl *D) { return false; if (const NamedDecl *ND = dyn_cast(D)) { - switch (ND->getLinkage()) { + switch (ND->getFormalLinkage()) { case NoLinkage: case InternalLinkage: return true; case UniqueExternalLinkage: + llvm_unreachable("Not a sema linkage"); case ExternalLinkage: return false; } -- cgit v1.2.3 From d4494681ef9dd76d8168e8a96af5473fb58e2e79 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 13 May 2013 00:29:57 +0000 Subject: Add missing triple to CodeGen test. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181679 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/CodeGenCXX/cxx1y-deduced-return-type.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CodeGenCXX/cxx1y-deduced-return-type.cpp b/test/CodeGenCXX/cxx1y-deduced-return-type.cpp index edf673df4a..6d15a2246d 100644 --- a/test/CodeGenCXX/cxx1y-deduced-return-type.cpp +++ b/test/CodeGenCXX/cxx1y-deduced-return-type.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++1y -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -std=c++1y -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s // CHECK: @x = global {{.*}} zeroinitializer -- cgit v1.2.3 From 1fcf31efaf58fcdf274409cf8926915636bce033 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Mon, 13 May 2013 01:24:18 +0000 Subject: Update for LLVM interface change in r181680. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181681 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Parse/ParseStmt.cpp | 2 +- tools/driver/cc1as_main.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 43b6965d31..f47b0e5435 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -2091,8 +2091,8 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { const std::string &TT = TheTriple.getTriple(); const llvm::Target *TheTarget = llvm::TargetRegistry::lookupTarget(TT, Error); - OwningPtr MAI(TheTarget->createMCAsmInfo(TT)); OwningPtr MRI(TheTarget->createMCRegInfo(TT)); + OwningPtr MAI(TheTarget->createMCAsmInfo(*MRI, TT)); OwningPtr MOFI(new llvm::MCObjectFileInfo()); OwningPtr STI(TheTarget->createMCSubtargetInfo(TT, "", "")); diff --git a/tools/driver/cc1as_main.cpp b/tools/driver/cc1as_main.cpp index 232ea2f668..46ba2463f1 100644 --- a/tools/driver/cc1as_main.cpp +++ b/tools/driver/cc1as_main.cpp @@ -287,12 +287,12 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, // it later. SrcMgr.setIncludeDirs(Opts.IncludePaths); - OwningPtr MAI(TheTarget->createMCAsmInfo(Opts.Triple)); - assert(MAI && "Unable to create target asm info!"); - OwningPtr MRI(TheTarget->createMCRegInfo(Opts.Triple)); assert(MRI && "Unable to create target register info!"); + OwningPtr MAI(TheTarget->createMCAsmInfo(*MRI, Opts.Triple)); + assert(MAI && "Unable to create target asm info!"); + bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj; formatted_raw_ostream *Out = GetOutputStream(Opts, Diags, IsBinary); if (!Out) -- cgit v1.2.3 From 6af701f29be43e49a25ab098c79940ae4cbb69c7 Mon Sep 17 00:00:00 2001 From: Alexey Bataev Date: Mon, 13 May 2013 04:18:18 +0000 Subject: OpenMP threadprivate with qualified names. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181683 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclOpenMP.h | 26 ++-- include/clang/Basic/DiagnosticParseKinds.td | 5 +- include/clang/Basic/DiagnosticSemaKinds.td | 18 ++- include/clang/Parse/Parser.h | 11 +- include/clang/Sema/Sema.h | 17 +- lib/AST/DeclOpenMP.cpp | 10 +- lib/AST/DeclPrinter.cpp | 4 +- lib/Basic/OpenMPKinds.cpp | 3 +- lib/Parse/ParseOpenMP.cpp | 93 ++++++----- lib/Parse/Parser.cpp | 5 +- lib/Parse/RAIIObjectsForParser.h | 7 +- lib/Sema/SemaOpenMP.cpp | 230 +++++++++++++++------------- lib/Sema/SemaTemplateInstantiateDecl.cpp | 8 +- lib/Serialization/ASTReaderDecl.cpp | 4 +- test/OpenMP/predefined_macro.c | 1 - test/OpenMP/threadprivate_messages.cpp | 52 ++++--- 16 files changed, 271 insertions(+), 223 deletions(-) diff --git a/include/clang/AST/DeclOpenMP.h b/include/clang/AST/DeclOpenMP.h index ca92040c32..42fe907aa1 100644 --- a/include/clang/AST/DeclOpenMP.h +++ b/include/clang/AST/DeclOpenMP.h @@ -1,4 +1,4 @@ -//===--- OpenMP.h - Classes for representing OpenMP directives ---*- C++ -*-===// +//===- DeclOpenMP.h - Classes for representing OpenMP directives -*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file defines OpenMP nodes. +/// \brief This file defines OpenMP nodes for declarative directives. /// //===----------------------------------------------------------------------===// @@ -20,8 +20,6 @@ namespace clang { -class DeclRefExpr; - /// \brief This represents '#pragma omp threadprivate ...' directive. /// For example, in the following, both 'a' and 'A::b' are threadprivate: /// @@ -43,29 +41,29 @@ class OMPThreadPrivateDecl : public Decl { OMPThreadPrivateDecl(Kind DK, DeclContext *DC, SourceLocation L) : Decl(DK, DC, L), NumVars(0) { } - ArrayRef getVars() const { - return ArrayRef( - reinterpret_cast(this + 1), + ArrayRef getVars() const { + return ArrayRef( + reinterpret_cast(this + 1), NumVars); } - llvm::MutableArrayRef getVars() { - return llvm::MutableArrayRef( - reinterpret_cast(this + 1), + llvm::MutableArrayRef getVars() { + return llvm::MutableArrayRef( + reinterpret_cast(this + 1), NumVars); } - void setVars(ArrayRef VL); + void setVars(ArrayRef VL); public: static OMPThreadPrivateDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L, - ArrayRef VL); + ArrayRef VL); static OMPThreadPrivateDecl *CreateDeserialized(ASTContext &C, unsigned ID, unsigned N); - typedef llvm::MutableArrayRef::iterator varlist_iterator; - typedef ArrayRef::iterator varlist_const_iterator; + typedef llvm::MutableArrayRef::iterator varlist_iterator; + typedef ArrayRef::iterator varlist_const_iterator; unsigned varlist_size() const { return NumVars; } bool varlist_empty() const { return NumVars == 0; } diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index e001bd46a2..a4b8d1810c 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -819,13 +819,14 @@ def err_seh___finally_block : Error< def warn_pragma_omp_ignored : Warning < "unexpected '#pragma omp ...' in program">, InGroup, DefaultIgnore; def warn_omp_extra_tokens_at_eol : Warning < - "extra tokens at end of '#pragma omp %0' are ignored">, + "extra tokens at the end of '#pragma omp %0' are ignored">, InGroup; def err_omp_unknown_directive : Error < "expected an OpenMP directive">; def err_omp_unexpected_directive : Error < "unexpected OpenMP directive '#pragma omp %0'">; - +def err_omp_expected_var : Error < + "expected '#pragma omp %0' argument to be a variable name">; } // end of Parse Issue category. let CategoryName = "Modules Issue" in { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 18d5796ea1..c2728820d4 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -6336,24 +6336,26 @@ def err_wrong_sampler_addressspace: Error< "sampler type cannot be used with the __local and __global address space qualifiers">; def err_opencl_global_invalid_addr_space : Error< "global variables must have a constant address space qualifier">; - + +} // end of sema category + +let CategoryName = "OpenMP Issue" in { // OpenMP support. def err_omp_expected_var_arg_suggest : Error< "%0 is not a global variable, static local variable or static data member%select{|; did you mean %2?}1">; def err_omp_global_var_arg : Error< "arguments of '#pragma omp %0' must have %select{global storage|static storage duration}1">; def err_omp_ref_type_arg : Error< - "arguments of '#pragma omp %0' cannot be of reference type %1">; + "arguments of '#pragma omp %0' cannot be of reference type">; def err_omp_var_scope : Error< - "'#pragma omp %0' must appear in the scope of the %1 variable declaration">; + "'#pragma omp %0' must appear in the scope of the %q1 variable declaration">; def err_omp_var_used : Error< - "'#pragma omp %0' must precede all references to variable %1">; + "'#pragma omp %0' must precede all references to variable %q1">; def err_omp_var_thread_local : Error< "variable %0 cannot be threadprivate because it is thread-local">; -def err_omp_incomplete_type : Error< - "a threadprivate variable must not have incomplete type %0">; - -} // end of sema category +def err_omp_threadprivate_incomplete_type : Error< + "threadprivate variable with incomplete type %0">; +} // end of OpenMP category let CategoryName = "Related Result Type Issue" in { // Objective-C related result type compatibility diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 72acacf5ae..1a1d6d2ae1 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -2138,9 +2138,18 @@ private: //===--------------------------------------------------------------------===// // OpenMP: Directives and clauses. + /// \brief Parses declarative OpenMP directives. DeclGroupPtrTy ParseOpenMPDeclarativeDirective(); + /// \brief Parses simple list of variables. + /// + /// \param Kind Kind of the directive. + /// \param [out] VarList List of referenced variables. + /// \param AllowScopeSpecifier true, if the variables can have fully + /// qualified names. + /// bool ParseOpenMPSimpleVarList(OpenMPDirectiveKind Kind, - SmallVectorImpl &IdList); + SmallVectorImpl &VarList, + bool AllowScopeSpecifier); public: bool ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, bool AllowDestructorName, diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 78152313c8..c2300e977e 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -6682,16 +6682,19 @@ public: unsigned SpellingListIndex, bool IsPackExpansion); // OpenMP directives and clauses. - + /// \brief Called on correct id-expression from the '#pragma omp + /// threadprivate'. + ExprResult ActOnOpenMPIdExpression(Scope *CurScope, + CXXScopeSpec &ScopeSpec, + const DeclarationNameInfo &Id); /// \brief Called on well-formed '#pragma omp threadprivate'. DeclGroupPtrTy ActOnOpenMPThreadprivateDirective( - SourceLocation Loc, - Scope *CurScope, - ArrayRef IdList); - /// \brief Build a new OpenMPThreadPrivateDecl and check its correctness. + SourceLocation Loc, + ArrayRef VarList); + // \brief Builds a new OpenMPThreadPrivateDecl and checks its correctness. OMPThreadPrivateDecl *CheckOMPThreadPrivateDecl( - SourceLocation Loc, - ArrayRef VarList); + SourceLocation Loc, + ArrayRef VarList); /// \brief The kind of conversion being performed. enum CheckedConversionKind { diff --git a/lib/AST/DeclOpenMP.cpp b/lib/AST/DeclOpenMP.cpp index c0d10a0f41..522caefe3b 100644 --- a/lib/AST/DeclOpenMP.cpp +++ b/lib/AST/DeclOpenMP.cpp @@ -28,9 +28,9 @@ void OMPThreadPrivateDecl::anchor() { } OMPThreadPrivateDecl *OMPThreadPrivateDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, - ArrayRef VL) { + ArrayRef VL) { unsigned Size = sizeof(OMPThreadPrivateDecl) + - (VL.size() * sizeof(DeclRefExpr *)); + (VL.size() * sizeof(Expr *)); void *Mem = C.Allocate(Size, llvm::alignOf()); OMPThreadPrivateDecl *D = new (Mem) OMPThreadPrivateDecl(OMPThreadPrivate, @@ -43,7 +43,7 @@ OMPThreadPrivateDecl *OMPThreadPrivateDecl::Create(ASTContext &C, OMPThreadPrivateDecl *OMPThreadPrivateDecl::CreateDeserialized(ASTContext &C, unsigned ID, unsigned N) { - unsigned Size = sizeof(OMPThreadPrivateDecl) + (N * sizeof(DeclRefExpr *)); + unsigned Size = sizeof(OMPThreadPrivateDecl) + (N * sizeof(Expr *)); void *Mem = AllocateDeserializedDecl(C, ID, Size); OMPThreadPrivateDecl *D = new (Mem) OMPThreadPrivateDecl(OMPThreadPrivate, @@ -52,9 +52,9 @@ OMPThreadPrivateDecl *OMPThreadPrivateDecl::CreateDeserialized(ASTContext &C, return D; } -void OMPThreadPrivateDecl::setVars(ArrayRef VL) { +void OMPThreadPrivateDecl::setVars(ArrayRef VL) { assert(VL.size() == NumVars && "Number of variables is not the same as the preallocated buffer"); - DeclRefExpr **Vars = reinterpret_cast(this + 1); + Expr **Vars = reinterpret_cast(this + 1); std::copy(VL.begin(), VL.end(), Vars); } diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index d47972bc61..1c9fd2d5f6 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -1180,9 +1180,9 @@ void DeclPrinter::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) { if (!D->varlist_empty()) { for (OMPThreadPrivateDecl::varlist_iterator I = D->varlist_begin(), E = D->varlist_end(); - I != E; ++I) { + I != E; ++I) { Out << (I == D->varlist_begin() ? '(' : ',') - << *cast((*I)->getDecl()); + << *cast(cast(*I)->getDecl()); } Out << ")"; } diff --git a/lib/Basic/OpenMPKinds.cpp b/lib/Basic/OpenMPKinds.cpp index 835908d2a1..b90fbc7a7c 100644 --- a/lib/Basic/OpenMPKinds.cpp +++ b/lib/Basic/OpenMPKinds.cpp @@ -32,7 +32,7 @@ const char *clang::getOpenMPDirectiveName(OpenMPDirectiveKind Kind) { assert(Kind < NUM_OPENMP_DIRECTIVES); switch (Kind) { case OMPD_unknown: - return ("unknown"); + return "unknown"; #define OPENMP_DIRECTIVE(Name) \ case OMPD_##Name : return #Name; #include "clang/Basic/OpenMPKinds.def" @@ -41,3 +41,4 @@ const char *clang::getOpenMPDirectiveName(OpenMPDirectiveKind Kind) { } llvm_unreachable("Invalid OpenMP directive kind"); } + diff --git a/lib/Parse/ParseOpenMP.cpp b/lib/Parse/ParseOpenMP.cpp index 507a6b1bcd..e192ae2e23 100644 --- a/lib/Parse/ParseOpenMP.cpp +++ b/lib/Parse/ParseOpenMP.cpp @@ -12,8 +12,10 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTConsumer.h" -#include "clang/Parse/Parser.h" #include "clang/Parse/ParseDiagnostic.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/Scope.h" +#include "llvm/ADT/PointerIntPair.h" #include "RAIIObjectsForParser.h" using namespace clang; @@ -21,22 +23,24 @@ using namespace clang; // OpenMP declarative directives. //===----------------------------------------------------------------------===// -/// \brief Parses OpenMP declarative directive -/// threadprivate-directive -/// annot_pragma_openmp threadprivate simple-variable-list +/// \brief Parsing of declarative OpenMP directives. +/// +/// threadprivate-directive: +/// annot_pragma_openmp 'threadprivate' simple-variable-list /// Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() { assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!"); SourceLocation Loc = ConsumeToken(); - SmallVector Identifiers; - OpenMPDirectiveKind Kind = Tok.isAnnotation() ? - OMPD_unknown : - getOpenMPDirectiveKind(PP.getSpelling(Tok)); - switch(Kind) { + SmallVector Identifiers; + OpenMPDirectiveKind DKind = Tok.isAnnotation() ? + OMPD_unknown : + getOpenMPDirectiveKind(PP.getSpelling(Tok)); + + switch (DKind) { case OMPD_threadprivate: ConsumeToken(); - if (!ParseOpenMPSimpleVarList(OMPD_threadprivate, Identifiers)) { + if (!ParseOpenMPSimpleVarList(OMPD_threadprivate, Identifiers, true)) { // The last seen token is annot_pragma_openmp_end - need to check for // extra tokens. if (Tok.isNot(tok::annot_pragma_openmp_end)) { @@ -44,9 +48,9 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() { << getOpenMPDirectiveName(OMPD_threadprivate); SkipUntil(tok::annot_pragma_openmp_end, false, true); } + // Skip the last annot_pragma_openmp_end. ConsumeToken(); return Actions.ActOnOpenMPThreadprivateDirective(Loc, - getCurScope(), Identifiers); } break; @@ -55,7 +59,7 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() { break; default: Diag(Tok, diag::err_omp_unexpected_directive) - << getOpenMPDirectiveName(Kind); + << getOpenMPDirectiveName(DKind); break; } SkipUntil(tok::annot_pragma_openmp_end, false); @@ -63,56 +67,69 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirective() { } /// \brief Parses list of simple variables for '#pragma omp threadprivate' -/// directive -/// simple-variable-list: -/// ( unqualified-id {, unqualified-id} ) annot_pragma_openmp_end +/// directive. +/// +/// simple-variable-list: +/// '(' id-expression {, id-expression} ')' /// -bool Parser::ParseOpenMPSimpleVarList( - OpenMPDirectiveKind Kind, - SmallVectorImpl &IdList) { +bool Parser::ParseOpenMPSimpleVarList(OpenMPDirectiveKind Kind, + SmallVectorImpl &VarList, + bool AllowScopeSpecifier) { + VarList.clear(); // Parse '('. - bool IsCorrect = true; - BalancedDelimiterTracker T(*this, tok::l_paren); - if (T.expectAndConsume(diag::err_expected_lparen_after, - getOpenMPDirectiveName(Kind))) { - SkipUntil(tok::annot_pragma_openmp_end, false, true); - return false; - } + BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); + bool LParen = !T.expectAndConsume(diag::err_expected_lparen_after, + getOpenMPDirectiveName(Kind)); + bool IsCorrect = LParen; + bool NoIdentIsFound = true; // Read tokens while ')' or annot_pragma_openmp_end is not found. - do { + while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) { CXXScopeSpec SS; SourceLocation TemplateKWLoc; UnqualifiedId Name; // Read var name. Token PrevTok = Tok; + NoIdentIsFound = false; - if (ParseUnqualifiedId(SS, false, false, false, ParsedType(), - TemplateKWLoc, Name)) { + if (AllowScopeSpecifier && getLangOpts().CPlusPlus && + ParseOptionalCXXScopeSpecifier(SS, ParsedType(), false)) { IsCorrect = false; SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, false, true); - } - else if (Tok.isNot(tok::comma) && Tok.isNot(tok::r_paren) && - Tok.isNot(tok::annot_pragma_openmp_end)) { + } else if (ParseUnqualifiedId(SS, false, false, false, ParsedType(), + TemplateKWLoc, Name)) { + IsCorrect = false; + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + false, true); + } else if (Tok.isNot(tok::comma) && Tok.isNot(tok::r_paren) && + Tok.isNot(tok::annot_pragma_openmp_end)) { IsCorrect = false; SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, false, true); - Diag(PrevTok.getLocation(), diag::err_expected_unqualified_id) - << getLangOpts().CPlusPlus + Diag(PrevTok.getLocation(), diag::err_expected_ident) << SourceRange(PrevTok.getLocation(), PrevTokLocation); } else { - IdList.push_back(Actions.GetNameFromUnqualifiedId(Name)); + DeclarationNameInfo NameInfo = Actions.GetNameFromUnqualifiedId(Name); + ExprResult Res = Actions.ActOnOpenMPIdExpression(getCurScope(), SS, + NameInfo); + if (Res.isUsable()) + VarList.push_back(Res.take()); } // Consume ','. if (Tok.is(tok::comma)) { ConsumeToken(); } - } while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)); + } - if (IsCorrect || Tok.is(tok::r_paren)) { - IsCorrect = !T.consumeClose() && IsCorrect; + if (NoIdentIsFound) { + Diag(Tok, diag::err_expected_ident); + IsCorrect = false; } - return !IsCorrect && IdList.empty(); + // Parse ')'. + IsCorrect = ((LParen || Tok.is(tok::r_paren)) && !T.consumeClose()) + && IsCorrect; + + return !IsCorrect && VarList.empty(); } diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 2117df45f3..a5371f9e61 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1897,7 +1897,7 @@ bool BalancedDelimiterTracker::diagnoseOverflow() { P.Diag(P.Tok, diag::err_bracket_depth_exceeded) << P.getLangOpts().BracketDepth; P.Diag(P.Tok, diag::note_bracket_depth); - P.SkipUntil(tok::eof); + P.SkipUntil(tok::eof, FinalToken); return true; } @@ -1927,7 +1927,8 @@ bool BalancedDelimiterTracker::diagnoseMissingClose() { } P.Diag(P.Tok, DID); P.Diag(LOpen, diag::note_matching) << LHSName; - if (P.SkipUntil(Close, /*StopAtSemi*/ true, /*DontConsume*/ true)) + if (P.SkipUntil(Close, FinalToken, /*StopAtSemi*/ true, /*DontConsume*/ true) + && P.Tok.is(Close)) LClose = P.ConsumeAnyToken(); return true; } diff --git a/lib/Parse/RAIIObjectsForParser.h b/lib/Parse/RAIIObjectsForParser.h index 213950a6db..f68a2e09fe 100644 --- a/lib/Parse/RAIIObjectsForParser.h +++ b/lib/Parse/RAIIObjectsForParser.h @@ -358,7 +358,7 @@ namespace clang { /// pair, such as braces { ... } or parentheses ( ... ). class BalancedDelimiterTracker : public GreaterThanIsOperatorScope { Parser& P; - tok::TokenKind Kind, Close; + tok::TokenKind Kind, Close, FinalToken; SourceLocation (Parser::*Consumer)(); SourceLocation LOpen, LClose; @@ -377,9 +377,10 @@ namespace clang { bool diagnoseMissingClose(); public: - BalancedDelimiterTracker(Parser& p, tok::TokenKind k) + BalancedDelimiterTracker(Parser& p, tok::TokenKind k, + tok::TokenKind FinalToken = tok::semi) : GreaterThanIsOperatorScope(p.GreaterThanIsOperator, true), - P(p), Kind(k) + P(p), Kind(k), FinalToken(FinalToken) { switch (Kind) { default: llvm_unreachable("Unexpected balanced token"); diff --git a/lib/Sema/SemaOpenMP.cpp b/lib/Sema/SemaOpenMP.cpp index c815d4f9ab..4953d2d14e 100644 --- a/lib/Sema/SemaOpenMP.cpp +++ b/lib/Sema/SemaOpenMP.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// \file /// \brief This file implements semantic analysis for OpenMP directives and -/// clauses +/// clauses. /// //===----------------------------------------------------------------------===// @@ -22,117 +22,121 @@ using namespace clang; namespace { - class VarDeclFilterCCC : public CorrectionCandidateCallback { - private: - Sema &Actions; - public: - VarDeclFilterCCC(Sema &S) : Actions(S) { } - virtual bool ValidateCandidate(const TypoCorrection &Candidate) { - NamedDecl *ND = Candidate.getCorrectionDecl(); - if (VarDecl *VD = dyn_cast_or_null(ND)) { - return VD->hasGlobalStorage() && - Actions.isDeclInScope(ND, Actions.getCurLexicalContext(), - Actions.getCurScope()); - } - return false; - } - }; +class VarDeclFilterCCC : public CorrectionCandidateCallback { +private: + Sema &Actions; +public: + VarDeclFilterCCC(Sema &S) : Actions(S) { } + virtual bool ValidateCandidate(const TypoCorrection &Candidate) { + NamedDecl *ND = Candidate.getCorrectionDecl(); + if (VarDecl *VD = dyn_cast_or_null(ND)) { + return VD->hasGlobalStorage() && + Actions.isDeclInScope(ND, Actions.getCurLexicalContext(), + Actions.getCurScope()); + } + return false; + } +}; } -Sema::DeclGroupPtrTy Sema::ActOnOpenMPThreadprivateDirective( - SourceLocation Loc, - Scope *CurScope, - ArrayRef IdList) { - SmallVector Vars; - for (ArrayRef::iterator I = IdList.begin(), - E = IdList.end(); - I != E; ++I) { - LookupResult Lookup(*this, *I, LookupOrdinaryName); - LookupParsedName(Lookup, CurScope, NULL, true); - if (Lookup.isAmbiguous()) - continue; +ExprResult Sema::ActOnOpenMPIdExpression(Scope *CurScope, + CXXScopeSpec &ScopeSpec, + const DeclarationNameInfo &Id) { + LookupResult Lookup(*this, Id, LookupOrdinaryName); + LookupParsedName(Lookup, CurScope, &ScopeSpec, true); - VarDecl *VD; - if (!Lookup.isSingleResult()) { - VarDeclFilterCCC Validator(*this); - TypoCorrection Corrected = CorrectTypo(*I, LookupOrdinaryName, CurScope, - 0, Validator); - std::string CorrectedStr = Corrected.getAsString(getLangOpts()); - std::string CorrectedQuotedStr = Corrected.getQuoted(getLangOpts()); - if (Lookup.empty()) { - if (Corrected.isResolved()) { - Diag(I->getLoc(), diag::err_undeclared_var_use_suggest) - << I->getName() << CorrectedQuotedStr - << FixItHint::CreateReplacement(I->getLoc(), CorrectedStr); - } else { - Diag(I->getLoc(), diag::err_undeclared_var_use) - << I->getName(); - } + if (Lookup.isAmbiguous()) + return ExprError(); + + VarDecl *VD; + if (!Lookup.isSingleResult()) { + VarDeclFilterCCC Validator(*this); + TypoCorrection Corrected = CorrectTypo(Id, LookupOrdinaryName, CurScope, + 0, Validator); + std::string CorrectedStr = Corrected.getAsString(getLangOpts()); + std::string CorrectedQuotedStr = Corrected.getQuoted(getLangOpts()); + if (Lookup.empty()) { + if (Corrected.isResolved()) { + Diag(Id.getLoc(), diag::err_undeclared_var_use_suggest) + << Id.getName() << CorrectedQuotedStr + << FixItHint::CreateReplacement(Id.getLoc(), CorrectedStr); } else { - Diag(I->getLoc(), diag::err_omp_expected_var_arg_suggest) - << I->getName() << Corrected.isResolved() << CorrectedQuotedStr - << FixItHint::CreateReplacement(I->getLoc(), CorrectedStr); + Diag(Id.getLoc(), diag::err_undeclared_var_use) + << Id.getName(); } - if (!Corrected.isResolved()) continue; - VD = Corrected.getCorrectionDeclAs(); } else { - if (!(VD = Lookup.getAsSingle())) { - Diag(I->getLoc(), diag::err_omp_expected_var_arg_suggest) - << I->getName() << 0; - Diag(Lookup.getFoundDecl()->getLocation(), diag::note_declared_at); - continue; - } + Diag(Id.getLoc(), diag::err_omp_expected_var_arg_suggest) + << Id.getName() << Corrected.isResolved() << CorrectedQuotedStr + << FixItHint::CreateReplacement(Id.getLoc(), CorrectedStr); } - - // OpenMP [2.9.2, Syntax, C/C++] - // Variables must be file-scope, namespace-scope, or static block-scope. - if (!VD->hasGlobalStorage()) { - Diag(I->getLoc(), diag::err_omp_global_var_arg) - << getOpenMPDirectiveName(OMPD_threadprivate) - << !VD->isStaticLocal(); - Diag(VD->getLocation(), diag::note_forward_declaration) << VD; - continue; + if (!Corrected.isResolved()) return ExprError(); + VD = Corrected.getCorrectionDeclAs(); + } else { + if (!(VD = Lookup.getAsSingle())) { + Diag(Id.getLoc(), diag::err_omp_expected_var_arg_suggest) + << Id.getName() << 0; + Diag(Lookup.getFoundDecl()->getLocation(), diag::note_declared_at); + return ExprError(); } + } + Lookup.suppressDiagnostics(); - // OpenMP [2.9.2, Restrictions, C/C++, p.2] - // A threadprivate directive for file-scope variables must appear outside - // any definition or declaration. - // OpenMP [2.9.2, Restrictions, C/C++, p.3] - // A threadprivate directive for static class member variables must appear - // in the class definition, in the same scope in which the member - // variables are declared. - // OpenMP [2.9.2, Restrictions, C/C++, p.4] - // A threadprivate directive for namespace-scope variables must appear - // outside any definition or declaration other than the namespace - // definition itself. - // OpenMP [2.9.2, Restrictions, C/C++, p.6] - // A threadprivate directive for static block-scope variables must appear - // in the scope of the variable and not in a nested scope. - NamedDecl *ND = cast(VD); - if (!isDeclInScope(ND, getCurLexicalContext(), CurScope)) { - Diag(I->getLoc(), diag::err_omp_var_scope) - << getOpenMPDirectiveName(OMPD_threadprivate) << VD; - Diag(VD->getLocation(), diag::note_forward_declaration) << VD; - continue; - } + // OpenMP [2.9.2, Syntax, C/C++] + // Variables must be file-scope, namespace-scope, or static block-scope. + if (!VD->hasGlobalStorage()) { + Diag(Id.getLoc(), diag::err_omp_global_var_arg) + << getOpenMPDirectiveName(OMPD_threadprivate) + << !VD->isStaticLocal(); + bool IsDecl = VD->isThisDeclarationADefinition(Context) == + VarDecl::DeclarationOnly; + Diag(VD->getLocation(), + IsDecl ? diag::note_previous_decl : diag::note_defined_here) << VD; + return ExprError(); + } - // OpenMP [2.9.2, Restrictions, C/C++, p.2-6] - // A threadprivate directive must lexically precede all references to any - // of the variables in its list. - if (VD->isUsed()) { - Diag(I->getLoc(), diag::err_omp_var_used) - << getOpenMPDirectiveName(OMPD_threadprivate) << VD; - continue; - } + // OpenMP [2.9.2, Restrictions, C/C++, p.2] + // A threadprivate directive for file-scope variables must appear outside + // any definition or declaration. + // OpenMP [2.9.2, Restrictions, C/C++, p.3] + // A threadprivate directive for static class member variables must appear + // in the class definition, in the same scope in which the member + // variables are declared. + // OpenMP [2.9.2, Restrictions, C/C++, p.4] + // A threadprivate directive for namespace-scope variables must appear + // outside any definition or declaration other than the namespace + // definition itself. + // OpenMP [2.9.2, Restrictions, C/C++, p.6] + // A threadprivate directive for static block-scope variables must appear + // in the scope of the variable and not in a nested scope. + NamedDecl *ND = cast(VD); + if (!isDeclInScope(ND, getCurLexicalContext(), CurScope)) { + Diag(Id.getLoc(), diag::err_omp_var_scope) + << getOpenMPDirectiveName(OMPD_threadprivate) << VD; + bool IsDecl = VD->isThisDeclarationADefinition(Context) == + VarDecl::DeclarationOnly; + Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl : + diag::note_defined_here) << VD; + return ExprError(); + } - QualType ExprType = VD->getType().getNonReferenceType(); - DeclRefExpr *Var = cast(BuildDeclRefExpr(VD, - ExprType, - VK_RValue, - I->getLoc()).take()); - Vars.push_back(Var); + // OpenMP [2.9.2, Restrictions, C/C++, p.2-6] + // A threadprivate directive must lexically precede all references to any + // of the variables in its list. + if (VD->isUsed()) { + Diag(Id.getLoc(), diag::err_omp_var_used) + << getOpenMPDirectiveName(OMPD_threadprivate) << VD; + return ExprError(); } - if (OMPThreadPrivateDecl *D = CheckOMPThreadPrivateDecl(Loc, Vars)) { + + QualType ExprType = VD->getType().getNonReferenceType(); + ExprResult DE = BuildDeclRefExpr(VD, ExprType, VK_RValue, Id.getLoc()); + return DE; +} + +Sema::DeclGroupPtrTy Sema::ActOnOpenMPThreadprivateDirective( + SourceLocation Loc, + ArrayRef VarList) { + if (OMPThreadPrivateDecl *D = CheckOMPThreadPrivateDecl(Loc, VarList)) { CurContext->addDecl(D); return DeclGroupPtrTy::make(DeclGroupRef(D)); } @@ -141,18 +145,19 @@ Sema::DeclGroupPtrTy Sema::ActOnOpenMPThreadprivateDirective( OMPThreadPrivateDecl *Sema::CheckOMPThreadPrivateDecl( SourceLocation Loc, - ArrayRef VarList) { - SmallVector Vars; - for (ArrayRef::iterator I = VarList.begin(), + ArrayRef VarList) { + SmallVector Vars; + for (ArrayRef::iterator I = VarList.begin(), E = VarList.end(); I != E; ++I) { - VarDecl *VD = cast((*I)->getDecl()); - SourceLocation ILoc = (*I)->getLocation(); + DeclRefExpr *DE = cast(*I); + VarDecl *VD = cast(DE->getDecl()); + SourceLocation ILoc = DE->getExprLoc(); // OpenMP [2.9.2, Restrictions, C/C++, p.10] // A threadprivate variable must not have an incomplete type. if (RequireCompleteType(ILoc, VD->getType(), - diag::err_omp_incomplete_type)) { + diag::err_omp_threadprivate_incomplete_type)) { continue; } @@ -160,15 +165,21 @@ OMPThreadPrivateDecl *Sema::CheckOMPThreadPrivateDecl( // A threadprivate variable must not have a reference type. if (VD->getType()->isReferenceType()) { Diag(ILoc, diag::err_omp_ref_type_arg) - << getOpenMPDirectiveName(OMPD_threadprivate) << VD->getType(); - Diag(VD->getLocation(), diag::note_forward_declaration) << VD; + << getOpenMPDirectiveName(OMPD_threadprivate); + bool IsDecl = VD->isThisDeclarationADefinition(Context) == + VarDecl::DeclarationOnly; + Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl : + diag::note_defined_here) << VD; continue; } // Check if this is a TLS variable. if (VD->getTLSKind()) { Diag(ILoc, diag::err_omp_var_thread_local) << VD; - Diag(VD->getLocation(), diag::note_forward_declaration) << VD; + bool IsDecl = VD->isThisDeclarationADefinition(Context) == + VarDecl::DeclarationOnly; + Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl : + diag::note_defined_here) << VD; continue; } @@ -179,3 +190,4 @@ OMPThreadPrivateDecl *Sema::CheckOMPThreadPrivateDecl( getCurLexicalContext(), Loc, Vars); } + diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 42e1757bcb..ff59cdcbc4 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2231,13 +2231,13 @@ Decl *TemplateDeclInstantiator::VisitClassScopeFunctionSpecializationDecl( Decl *TemplateDeclInstantiator::VisitOMPThreadPrivateDecl( OMPThreadPrivateDecl *D) { - SmallVector Vars; - for (ArrayRef::iterator I = D->varlist_begin(), - E = D->varlist_end(); + SmallVector Vars; + for (ArrayRef::iterator I = D->varlist_begin(), + E = D->varlist_end(); I != E; ++I) { Expr *Var = SemaRef.SubstExpr(*I, TemplateArgs).take(); assert(isa(Var) && "threadprivate arg is not a DeclRefExpr"); - Vars.push_back(cast(Var)); + Vars.push_back(Var); } OMPThreadPrivateDecl *TD = diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index e3f9f43a3b..b117b733a4 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1644,10 +1644,10 @@ void ASTDeclReader::mergeRedeclarable(Redeclarable *D, void ASTDeclReader::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) { VisitDecl(D); unsigned NumVars = D->varlist_size(); - SmallVector Vars; + SmallVector Vars; Vars.reserve(NumVars); for (unsigned i = 0; i != NumVars; ++i) { - Vars.push_back(cast(Reader.ReadExpr(F))); + Vars.push_back(Reader.ReadExpr(F)); } D->setVars(Vars); } diff --git a/test/OpenMP/predefined_macro.c b/test/OpenMP/predefined_macro.c index cf6c0cc611..3a81186209 100644 --- a/test/OpenMP/predefined_macro.c +++ b/test/OpenMP/predefined_macro.c @@ -31,4 +31,3 @@ #error "_OPENMP macro is defined without -fopenmp option" #endif // _OPENMP #endif // FOPENMP - diff --git a/test/OpenMP/threadprivate_messages.cpp b/test/OpenMP/threadprivate_messages.cpp index 0c448b2ef2..1bfba6d864 100644 --- a/test/OpenMP/threadprivate_messages.cpp +++ b/test/OpenMP/threadprivate_messages.cpp @@ -1,8 +1,8 @@ // RUN: %clang_cc1 -triple x86_64-apple-macos10.7.0 -verify -fopenmp -ferror-limit 100 %s -#pragma omp threadprivate // expected-error {{expected '(' after 'threadprivate'}} -#pragma omp threadprivate( // expected-error {{expected unqualified-id}} -#pragma omp threadprivate() // expected-error {{expected unqualified-id}} +#pragma omp threadprivate // expected-error {{expected '(' after 'threadprivate'}} expected-error {{expected identifier}} +#pragma omp threadprivate( // expected-error {{expected identifier}} expected-error {{expected ')'}} expected-note {{to match this '('}} +#pragma omp threadprivate() // expected-error {{expected identifier}} #pragma omp threadprivate(1) // expected-error {{expected unqualified-id}} struct CompleteSt{ int a; @@ -11,27 +11,27 @@ struct CompleteSt{ struct CompleteSt1{ #pragma omp threadprivate(1) // expected-error {{expected unqualified-id}} int a; -} d; // expected-note {{forward declaration of 'd'}} +} d; // expected-note {{'d' defined here}} -int a; // expected-note {{forward declaration of 'a'}} +int a; // expected-note {{'a' defined here}} #pragma omp threadprivate(a) #pragma omp threadprivate(u) // expected-error {{use of undeclared identifier 'u'}} #pragma omp threadprivate(d, a) // expected-error {{'#pragma omp threadprivate' must precede all references to variable 'a'}} int foo() { // expected-note {{declared here}} static int l; -#pragma omp threadprivate(l)) // expected-warning {{extra tokens at end of '#pragma omp threadprivate' are ignored}} +#pragma omp threadprivate(l)) // expected-warning {{extra tokens at the end of '#pragma omp threadprivate' are ignored}} return (a); } -#pragma omp threadprivate a // expected-error {{expected '(' after 'threadprivate'}} +#pragma omp threadprivate a // expected-error {{expected '(' after 'threadprivate'}} expected-error {{'#pragma omp threadprivate' must precede all references to variable 'a'}} #pragma omp threadprivate(d // expected-error {{expected ')'}} expected-note {{to match this '('}} expected-error {{'#pragma omp threadprivate' must precede all references to variable 'd'}} -#pragma omp threadprivate(d)) +#pragma omp threadprivate(d)) // expected-error {{'#pragma omp threadprivate' must precede all references to variable 'd'}} expected-warning {{extra tokens at the end of '#pragma omp threadprivate' are ignored}} int x, y; -#pragma omp threadprivate(x)) // expected-warning {{extra tokens at end of '#pragma omp threadprivate' are ignored}} -#pragma omp threadprivate(y)), // expected-warning {{extra tokens at end of '#pragma omp threadprivate' are ignored}} +#pragma omp threadprivate(x)) // expected-warning {{extra tokens at the end of '#pragma omp threadprivate' are ignored}} +#pragma omp threadprivate(y)), // expected-warning {{extra tokens at the end of '#pragma omp threadprivate' are ignored}} #pragma omp threadprivate(a,d) // expected-error {{'#pragma omp threadprivate' must precede all references to variable 'a'}} expected-error {{'#pragma omp threadprivate' must precede all references to variable 'd'}} -#pragma omp threadprivate(d.a) // expected-error {{expected unqualified-id}} +#pragma omp threadprivate(d.a) // expected-error {{expected identifier}} #pragma omp threadprivate((float)a) // expected-error {{expected unqualified-id}} int foa; #pragma omp threadprivate(faa) // expected-error {{use of undeclared identifier 'faa'; did you mean 'foa'?}} @@ -41,31 +41,31 @@ int foa; struct IncompleteSt; // expected-note {{forward declaration of 'IncompleteSt'}} extern IncompleteSt e; -#pragma omp threadprivate (e) // expected-error {{a threadprivate variable must not have incomplete type 'IncompleteSt'}} +#pragma omp threadprivate (e) // expected-error {{threadprivate variable with incomplete type 'IncompleteSt'}} -int &f = a; // expected-note {{forward declaration of 'f'}} -#pragma omp threadprivate (f) // expected-error {{arguments of '#pragma omp threadprivate' cannot be of reference type 'int &'}} +int &f = a; // expected-note {{'f' defined here}} +#pragma omp threadprivate (f) // expected-error {{arguments of '#pragma omp threadprivate' cannot be of reference type}} class Class { private: int a; // expected-note {{declared here}} - static int b; + static int b; // expected-note {{'b' declared here}} Class() : a(0){} public: Class (int aaa) : a(aaa) {} #pragma omp threadprivate (b, a) // expected-error {{'a' is not a global variable, static local variable or static data member}} } g(10); #pragma omp threadprivate (b) // expected-error {{use of undeclared identifier 'b'}} -#pragma omp threadprivate (Class::b) // expected-error {{expected unqualified-id}} +#pragma omp threadprivate (Class::b) // expected-error {{'#pragma omp threadprivate' must appear in the scope of the 'Class::b' variable declaration}} #pragma omp threadprivate (g) namespace ns { - int m; + int m; // expected-note 2 {{'m' defined here}} #pragma omp threadprivate (m) } #pragma omp threadprivate (m) // expected-error {{use of undeclared identifier 'm'}} -#pragma omp threadprivate (ns::m) // expected-error {{expected unqualified-id}} -#pragma omp threadprivate (ns:m) // expected-error {{expected unqualified-id}} +#pragma omp threadprivate (ns::m) // expected-error {{'#pragma omp threadprivate' must appear in the scope of the 'ns::m' variable declaration}} +#pragma omp threadprivate (ns:m) // expected-error {{unexpected ':' in nested name specifier; did you mean '::'?}} expected-error {{'#pragma omp threadprivate' must appear in the scope of the 'ns::m' variable declaration}} const int h = 12; const volatile int i = 10; @@ -84,26 +84,30 @@ class TempClass { }; #pragma omp threadprivate (s) // expected-error {{use of undeclared identifier 's'}} -static __thread int t; // expected-note {{forward declaration of 't'}} +static __thread int t; // expected-note {{'t' defined here}} #pragma omp threadprivate (t) // expected-error {{variable 't' cannot be threadprivate because it is thread-local}} int o; // expected-note {{candidate found by name lookup is 'o'}} +#pragma omp threadprivate (o) namespace { int o; // expected-note {{candidate found by name lookup is '::o'}} +#pragma omp threadprivate (o) +#pragma omp threadprivate (o) // expected-error {{'#pragma omp threadprivate' must precede all references to variable '::o'}} } #pragma omp threadprivate (o) // expected-error {{reference to 'o' is ambiguous}} +#pragma omp threadprivate (::o) // expected-error {{'#pragma omp threadprivate' must precede all references to variable 'o'}} -int main(int argc, char **argv) { // expected-note {{forward declaration of 'argc'}} +int main(int argc, char **argv) { // expected-note {{'argc' defined here}} - int x, y = argc; // expected-note {{forward declaration of 'y'}} + int x, y = argc; // expected-note {{'y' defined here}} static double d1; static double d2; - static double d3; // expected-note {{forward declaration of 'd3'}} + static double d3; // expected-note {{'d3' defined here}} d.a = a; d2++; ; -#pragma omp threadprivate(argc+y) // expected-error {{expected unqualified-id}} +#pragma omp threadprivate(argc+y) // expected-error {{expected identifier}} #pragma omp threadprivate(argc,y) // expected-error 2 {{arguments of '#pragma omp threadprivate' must have static storage duration}} #pragma omp threadprivate(d2) // expected-error {{'#pragma omp threadprivate' must precede all references to variable 'd2'}} #pragma omp threadprivate(d1) -- cgit v1.2.3 From f8aa155e40bc98844f46ecbfdbe430696c36ce24 Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Mon, 13 May 2013 06:57:50 +0000 Subject: Debug Info: PR14992: Support values for non-type template parameters of function type git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181685 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGDebugInfo.cpp | 4 +++- test/CodeGenCXX/debug-info-template.cpp | 23 +++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 13b681edc9..583b23980e 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1220,9 +1220,11 @@ CollectTemplateParams(const TemplateParameterList *TPList, V = CGM.GetAddrOfGlobalVar(VD); // Member function pointers have special support for building them, though // this is currently unsupported in LLVM CodeGen. - if (InstanceMember) + if (InstanceMember) { if (const CXXMethodDecl *method = dyn_cast(D)) V = CGM.getCXXABI().EmitMemberPointer(method); + } else if (const FunctionDecl *FD = dyn_cast(D)) + V = CGM.GetAddrOfFunction(FD); // Member data pointers have special handling too to compute the fixed // offset within the object. if (isa(D)) { diff --git a/test/CodeGenCXX/debug-info-template.cpp b/test/CodeGenCXX/debug-info-template.cpp index 1d2dc10cf2..a97c991f38 100644 --- a/test/CodeGenCXX/debug-info-template.cpp +++ b/test/CodeGenCXX/debug-info-template.cpp @@ -2,6 +2,9 @@ // CHECK: [[EMPTY:![0-9]*]] = metadata !{i32 0} +// CHECK: [[FUNTYPE:![0-9]*]] = {{.*}}, metadata [[FUNARGS:![0-9]*]], i32 0, i32 0} ; [ DW_TAG_subroutine_type ] +// CHECK: [[FUNARGS]] = metadata !{null} + // func<...> doesn't have any template arguments listed since we don't support // packs yet. This could be encoded with GNU's // DW_TAG_GNU_template_parameter_pack extension. @@ -9,8 +12,8 @@ // CHECK: [[INT:![0-9]*]] = {{.*}} ; [ DW_TAG_base_type ] [int] // CHECK: metadata [[TCI:![0-9]*]], i32 0, i32 1, %class.TC* @tci, null} ; [ DW_TAG_variable ] [tci] -// CHECK: [[TC:![0-9]*]] = {{.*}}, metadata [[TCARGS:![0-9]*]]} ; [ DW_TAG_class_type ] [TC] -// CHECK: [[TCARGS]] = metadata !{metadata [[TCARG1:![0-9]*]], metadata [[TCARG2:![0-9]*]], metadata [[TCARG3:![0-9]*]], metadata [[TCARG4:![0-9]*]], metadata [[TCARG5:![0-9]*]]} +// CHECK: [[TC:![0-9]*]] = {{.*}}, metadata [[TCARGS:![0-9]*]]} ; [ DW_TAG_class_type ] [TC] +// CHECK: [[TCARGS]] = metadata !{metadata [[TCARG1:![0-9]*]], metadata [[TCARG2:![0-9]*]], metadata [[TCARG3:![0-9]*]], metadata [[TCARG4:![0-9]*]], metadata [[TCARG5:![0-9]*]], metadata [[TCARG6:![0-9]*]]} // // We seem to be missing file/line/col info on template value parameters - // metadata supports it but it's not populated. GCC doesn't emit it either, @@ -39,11 +42,13 @@ // // CHECK: [[TCARG5]] = {{.*}}metadata !"b", metadata [[MEMFUNPTR:![0-9]*]], { i64, i64 } { i64 ptrtoint (void (%struct.foo*)* @_ZN3foo1fEv to i64), i64 0 }, {{.*}} ; [ DW_TAG_template_value_parameter ] // CHECK: [[MEMFUNPTR]] = {{.*}}, metadata [[FTYPE]], metadata [[FOO]]} ; [ DW_TAG_ptr_to_member_type ] +// CHECK: [[TCARG6]] = {{.*}}metadata !"f", metadata [[FUNPTR:![0-9]*]], void ()* @_Z4funcv, {{.*}} ; [ DW_TAG_template_value_parameter ] +// CHECK: [[FUNPTR]] = {{.*}}, metadata [[FUNTYPE]]} ; [ DW_TAG_pointer_type ] // CHECK: metadata [[TCNT:![0-9]*]], i32 0, i32 1, %class.TC.0* @tcn, null} ; [ DW_TAG_variable ] [tcn] -// CHECK: [[TCNT:![0-9]*]] = {{.*}}, metadata [[TCNARGS:![0-9]*]]} ; [ DW_TAG_class_type ] [TC] -// CHECK: [[TCNARGS]] = metadata !{metadata [[TCNARG1:![0-9]*]], metadata [[TCNARG2:![0-9]*]], metadata [[TCNARG3:![0-9]*]], metadata [[TCNARG4:![0-9]*]], metadata [[TCNARG5:![0-9]*]]} +// CHECK: [[TCNT:![0-9]*]] = {{.*}}, metadata [[TCNARGS:![0-9]*]]} ; [ DW_TAG_class_type ] [TC] +// CHECK: [[TCNARGS]] = metadata !{metadata [[TCNARG1:![0-9]*]], metadata [[TCNARG2:![0-9]*]], metadata [[TCNARG3:![0-9]*]], metadata [[TCNARG4:![0-9]*]], metadata [[TCNARG5:![0-9]*]], metadata [[TCNARG6:![0-9]*]]} // CHECK: [[TCNARG1]] = {{.*}}metadata !"T", metadata [[INT]], {{.*}} ; [ DW_TAG_template_type_parameter ] // CHECK: [[TCNARG2]] = {{.*}}metadata !"", metadata [[INT]], i32 -3, {{.*}} ; [ DW_TAG_template_value_parameter ] // CHECK: [[TCNARG3]] = {{.*}}metadata !"x", metadata [[INTPTR]], i8 0, {{.*}} ; [ DW_TAG_template_value_parameter ] @@ -59,21 +64,23 @@ // naturally from the LLVM CodeGen side once we decide how to handle non-null // member function pointers. For now, it's simpler just to emit the 'i8 0'. // -// CHECK: [[TCNARG5]] = {{.*}}metadata !"b", metadata [[MEMFUNPTR:![0-9]*]], i8 0, {{.*}} ; [ DW_TAG_template_value_parameter ] +// CHECK: [[TCNARG5]] = {{.*}}metadata !"b", metadata [[MEMFUNPTR]], i8 0, {{.*}} ; [ DW_TAG_template_value_parameter ] +// CHECK: [[TCNARG6]] = {{.*}}metadata !"f", metadata [[FUNPTR]], i8 0, {{.*}} ; [ DW_TAG_template_value_parameter ] struct foo { char pad[8]; // make the member pointer to 'e' a bit more interesting (nonzero) int e; void f(); }; -template +template class TC { }; int glb; +void func(); -TC tci; -TC tcn; +TC tci; +TC tcn; template int func() { return 0; } int anchor = func(); -- cgit v1.2.3 From 378d93dcf7ec80661efc65642dc6266c9e13780a Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 13 May 2013 07:14:40 +0000 Subject: Assume macros to contain declarations. This seems to be the vastly more common case. If we find enough examples to the contrary, we can make it smarter. Before: #define MACRO void f(int * a) After: #define MACRO void f(int *a) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181687 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/TokenAnnotator.cpp | 1 + unittests/Format/FormatTest.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 12031166e2..29eaaa8b58 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -591,6 +591,7 @@ private: } } else if (Current.isOneOf(tok::kw_return, tok::kw_throw) || (Current.is(tok::l_paren) && !Line.MustBeDeclaration && + !Line.InPPDirective && (!Current.Parent || Current.Parent->isNot(tok::kw_for)))) { Contexts.back().IsExpression = true; } else if (Current.isOneOf(tok::r_paren, tok::greater, tok::comma)) { diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index f694c5b214..9b3fb9cc06 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -2638,10 +2638,16 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("for (int i = a * a; i < 10; ++i) {\n}"); verifyFormat("for (int i = 0; i < a * a; ++i) {\n}"); + verifyFormat("#define MACRO \\\n" + " int *i = a * b; \\\n" + " void f(a *b);", + getLLVMStyleWithColumns(19)); + verifyIndependentOfContext("A = new SomeType *[Length];"); verifyIndependentOfContext("A = new SomeType *[Length]();"); verifyGoogleFormat("A = new SomeType* [Length]();"); verifyGoogleFormat("A = new SomeType* [Length];"); + FormatStyle PointerLeft = getLLVMStyle(); PointerLeft.PointerBindsToType = true; verifyFormat("delete *x;", PointerLeft); -- cgit v1.2.3 From 07a64eca75d96ce77d1ce8cb73d1cb0877695d06 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Mon, 13 May 2013 08:42:42 +0000 Subject: Implements IndentWidth. This is required for various styles that are for example based on 8-indent. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181690 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Format/Format.h | 6 +++++- lib/Format/Format.cpp | 14 +++++++++++--- unittests/Format/FormatTest.cpp | 31 +++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index 9d17c77db7..dd8f666180 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -94,6 +94,9 @@ struct FormatStyle { /// Otherwise puts them into the right-most column. bool AlignEscapedNewlinesLeft; + /// \brief The number of characters to use for indentation. + unsigned IndentWidth; + bool operator==(const FormatStyle &R) const { return AccessModifierOffset == R.AccessModifierOffset && AlignEscapedNewlinesLeft == R.AlignEscapedNewlinesLeft && @@ -113,7 +116,8 @@ struct FormatStyle { PenaltyReturnTypeOnItsOwnLine == R.PenaltyReturnTypeOnItsOwnLine && PointerBindsToType == R.PointerBindsToType && SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments && - Standard == R.Standard; + Standard == R.Standard && + IndentWidth == IndentWidth; } }; diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 9a320f94bd..d5676814c1 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -85,6 +85,7 @@ template <> struct MappingTraits { IO.mapOptional("SpacesBeforeTrailingComments", Style.SpacesBeforeTrailingComments); IO.mapOptional("Standard", Style.Standard); + IO.mapOptional("IndentWidth", Style.IndentWidth); } }; } @@ -111,6 +112,7 @@ FormatStyle getLLVMStyle() { LLVMStyle.PointerBindsToType = false; LLVMStyle.SpacesBeforeTrailingComments = 1; LLVMStyle.Standard = FormatStyle::LS_Cpp03; + LLVMStyle.IndentWidth = 2; return LLVMStyle; } @@ -132,6 +134,7 @@ FormatStyle getGoogleStyle() { GoogleStyle.PointerBindsToType = true; GoogleStyle.SpacesBeforeTrailingComments = 2; GoogleStyle.Standard = FormatStyle::LS_Auto; + GoogleStyle.IndentWidth = 2; return GoogleStyle; } @@ -429,7 +432,7 @@ private: if (Newline) { unsigned WhitespaceStartColumn = State.Column; if (Current.is(tok::r_brace)) { - State.Column = Line.Level * 2; + State.Column = Line.Level * Style.IndentWidth; } else if (Current.is(tok::string_literal) && State.StartOfStringLiteral != 0) { State.Column = State.StartOfStringLiteral; @@ -604,6 +607,11 @@ private: Current.LastInChainOfCalls ? 0 : State.Column + Current.FormatTok.TokenLength; if (Current.Type == TT_CtorInitializerColon) { + // Indent 2 from the column, so: + // SomeClass::SomeClass() + // : First(...), ... + // Next(...) + // ^ line up here. State.Stack.back().Indent = State.Column + 2; if (Style.ConstructorInitializerAllOnOneLineOrOnePerLine) State.Stack.back().AvoidBinPacking = true; @@ -656,7 +664,7 @@ private: unsigned NewIndent; bool AvoidBinPacking; if (Current.is(tok::l_brace)) { - NewIndent = 2 + State.Stack.back().LastSpace; + NewIndent = Style.IndentWidth + State.Stack.back().LastSpace; AvoidBinPacking = false; } else { NewIndent = 4 + std::max(State.Stack.back().LastSpace, @@ -1253,7 +1261,7 @@ private: return IndentForLevel[Level]; if (Level == 0) return 0; - return getIndent(IndentForLevel, Level - 1) + 2; + return getIndent(IndentForLevel, Level - 1) + Style.IndentWidth; } /// \brief Get the offset of the line relatively to the level. diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 9b3fb9cc06..f0319d5e48 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -3959,6 +3959,36 @@ TEST_F(FormatTest, DoNotCreateUnreasonableUnwrappedLines) { "}"); } +TEST_F(FormatTest, FormatsClosingBracesInEmptyNestedBlocks) { + verifyFormat("class X {\n" + " void f() {\n" + " }\n" + "};", + getLLVMStyleWithColumns(12)); +} + +TEST_F(FormatTest, ConfigurableIndentWidth) { + FormatStyle EightIndent = getLLVMStyleWithColumns(18); + EightIndent.IndentWidth = 8; + verifyFormat("void f() {\n" + " someFunction();\n" + " if (true) {\n" + " f();\n" + " }\n" + "}", + EightIndent); + verifyFormat("class X {\n" + " void f() {\n" + " }\n" + "};", + EightIndent); + verifyFormat("int x[] = {\n" + " call(),\n" + " call(),\n" + "};", + EightIndent); +} + bool allStylesEqual(ArrayRef Styles) { for (size_t i = 1; i < Styles.size(); ++i) if (!(Styles[0] == Styles[i])) @@ -4022,6 +4052,7 @@ TEST_F(FormatTest, ParsesConfiguration) { PenaltyReturnTypeOnItsOwnLine, 1234u); CHECK_PARSE("SpacesBeforeTrailingComments: 1234", SpacesBeforeTrailingComments, 1234u); + CHECK_PARSE("IndentWidth: 32", IndentWidth, 32u); Style.Standard = FormatStyle::LS_Auto; CHECK_PARSE("Standard: C++03", Standard, FormatStyle::LS_Cpp03); -- cgit v1.2.3 From c3df5ffb52cb656c3a4d0b4d70f8f28ed2390e78 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 13 May 2013 09:19:24 +0000 Subject: Further improve optimization for nested calls. Fake parentheses (i.e. emulated parentheses used to correctly handle binary expressions) used to prevent the optimization implemented in r180264. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181692 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 32 ++++++++++++++++++++++---------- unittests/Format/FormatTest.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index d5676814c1..f3ca9c371f 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -270,7 +270,7 @@ private: AvoidBinPacking(AvoidBinPacking), BreakBeforeParameter(false), NoLineBreak(NoLineBreak), ColonPos(0), StartOfFunctionCall(0), NestedNameSpecifierContinuation(0), CallContinuation(0), - VariablePos(0) {} + VariablePos(0), ForFakeParenthesis(false) {} /// \brief The position to which a specific parenthesis level needs to be /// indented. @@ -329,6 +329,13 @@ private: /// Used to align further variables if necessary. unsigned VariablePos; + /// \brief \c true if this \c ParenState was created for a fake parenthesis. + /// + /// Does not need to be considered for memoization / the comparison function + /// as otherwise identical states will have the same fake/non-fake + /// \c ParenStates. + bool ForFakeParenthesis; + bool operator<(const ParenState &Other) const { if (Indent != Other.Indent) return Indent < Other.Indent; @@ -641,6 +648,7 @@ private: E = Current.FakeLParens.rend(); I != E; ++I) { ParenState NewParenState = State.Stack.back(); + NewParenState.ForFakeParenthesis = true; NewParenState.Indent = std::max(std::max(State.Column, NewParenState.Indent), State.Stack.back().LastSpace); @@ -662,27 +670,31 @@ private: // prepare for the following tokens. if (Current.opensScope()) { unsigned NewIndent; + unsigned LastSpace = State.Stack.back().LastSpace; bool AvoidBinPacking; if (Current.is(tok::l_brace)) { - NewIndent = Style.IndentWidth + State.Stack.back().LastSpace; + NewIndent = Style.IndentWidth + LastSpace; AvoidBinPacking = false; } else { - NewIndent = 4 + std::max(State.Stack.back().LastSpace, - State.Stack.back().StartOfFunctionCall); + NewIndent = + 4 + std::max(LastSpace, State.Stack.back().StartOfFunctionCall); AvoidBinPacking = !Style.BinPackParameters; } - State.Stack.push_back( - ParenState(NewIndent, State.Stack.back().LastSpace, AvoidBinPacking, - State.Stack.back().NoLineBreak)); if (Current.NoMoreTokensOnLevel && Current.FakeLParens.empty()) { // This parenthesis was the last token possibly making use of Indent and - // LastSpace of the next higher ParenLevel. Thus, erase them to acieve + // LastSpace of the next higher ParenLevel. Thus, erase them to achieve // better memoization results. - State.Stack[State.Stack.size() - 2].Indent = 0; - State.Stack[State.Stack.size() - 2].LastSpace = 0; + for (unsigned i = State.Stack.size() - 1; i > 0; --i) { + State.Stack[i].Indent = 0; + State.Stack[i].LastSpace = 0; + if (!State.Stack[i].ForFakeParenthesis) + break; + } } + State.Stack.push_back(ParenState(NewIndent, LastSpace, AvoidBinPacking, + State.Stack.back().NoLineBreak)); ++State.ParenLevel; } diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index f0319d5e48..931d29dd3e 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -1761,6 +1761,33 @@ TEST_F(FormatTest, MemoizationTests) { " aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(aaaaa(\n" " aaaaa())))))))))))))))))))))))))))))))))))))));", getLLVMStyleWithColumns(65)); + verifyFormat( + "aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa(\n" + " aaaaa,\n" + " aaaaa))))))))))));", + getLLVMStyleWithColumns(65)); // This test takes VERY long when memoization is broken. FormatStyle OnePerLine = getLLVMStyle(); -- cgit v1.2.3 From 7c9a93ec7cea816e94a0674909c312f3d0227864 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Mon, 13 May 2013 09:22:11 +0000 Subject: Implements UseTab for clang-format. This is required for kernel linux kernel style formatting. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181693 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Format/Format.h | 7 ++++++- lib/Format/Format.cpp | 3 +++ lib/Format/WhitespaceManager.cpp | 12 ++++++++++-- lib/Format/WhitespaceManager.h | 2 ++ unittests/Format/FormatTest.cpp | 22 ++++++++++++++++++++++ 5 files changed, 43 insertions(+), 3 deletions(-) diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index dd8f666180..79e26d0a0c 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -97,6 +97,10 @@ struct FormatStyle { /// \brief The number of characters to use for indentation. unsigned IndentWidth; + /// \brief If true, \c IndentWidth consecutive spaces will be replaced with + /// tab characters. + bool UseTab; + bool operator==(const FormatStyle &R) const { return AccessModifierOffset == R.AccessModifierOffset && AlignEscapedNewlinesLeft == R.AlignEscapedNewlinesLeft && @@ -117,7 +121,8 @@ struct FormatStyle { PointerBindsToType == R.PointerBindsToType && SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments && Standard == R.Standard && - IndentWidth == IndentWidth; + IndentWidth == R.IndentWidth && + UseTab == R.UseTab; } }; diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index f3ca9c371f..2f2f095780 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -86,6 +86,7 @@ template <> struct MappingTraits { Style.SpacesBeforeTrailingComments); IO.mapOptional("Standard", Style.Standard); IO.mapOptional("IndentWidth", Style.IndentWidth); + IO.mapOptional("UseTab", Style.UseTab); } }; } @@ -113,6 +114,7 @@ FormatStyle getLLVMStyle() { LLVMStyle.SpacesBeforeTrailingComments = 1; LLVMStyle.Standard = FormatStyle::LS_Cpp03; LLVMStyle.IndentWidth = 2; + LLVMStyle.UseTab = false; return LLVMStyle; } @@ -135,6 +137,7 @@ FormatStyle getGoogleStyle() { GoogleStyle.SpacesBeforeTrailingComments = 2; GoogleStyle.Standard = FormatStyle::LS_Auto; GoogleStyle.IndentWidth = 2; + GoogleStyle.UseTab = false; return GoogleStyle; } diff --git a/lib/Format/WhitespaceManager.cpp b/lib/Format/WhitespaceManager.cpp index a75c592bfe..21e38b1e97 100644 --- a/lib/Format/WhitespaceManager.cpp +++ b/lib/Format/WhitespaceManager.cpp @@ -122,7 +122,7 @@ void WhitespaceManager::addUntouchableComment(unsigned Column) { std::string WhitespaceManager::getNewLineText(unsigned NewLines, unsigned Spaces) { - return std::string(NewLines, '\n') + std::string(Spaces, ' '); + return std::string(NewLines, '\n') + getIndentText(Spaces); } std::string WhitespaceManager::getNewLineText(unsigned NewLines, @@ -139,7 +139,15 @@ std::string WhitespaceManager::getNewLineText(unsigned NewLines, Offset = 0; } } - return NewLineText + std::string(Spaces, ' '); + return NewLineText + getIndentText(Spaces); +} + +std::string WhitespaceManager::getIndentText(unsigned Spaces) { + if (!Style.UseTab) { + return std::string(Spaces, ' '); + } + return std::string(Spaces / Style.IndentWidth, '\t') + + std::string(Spaces % Style.IndentWidth, ' '); } void WhitespaceManager::alignComments() { diff --git a/lib/Format/WhitespaceManager.h b/lib/Format/WhitespaceManager.h index 5f3dc55eda..1b24b3f6d7 100644 --- a/lib/Format/WhitespaceManager.h +++ b/lib/Format/WhitespaceManager.h @@ -78,6 +78,8 @@ private: unsigned WhitespaceStartColumn, unsigned EscapedNewlineColumn); + std::string getIndentText(unsigned Spaces); + /// \brief Structure to store tokens for later layout and alignment. struct StoredToken { StoredToken(SourceLocation ReplacementLoc, unsigned ReplacementLength, diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 931d29dd3e..ce79a1c97b 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -4016,6 +4016,27 @@ TEST_F(FormatTest, ConfigurableIndentWidth) { EightIndent); } +TEST_F(FormatTest, ConfigurableUseOfTab) { + FormatStyle Tab = getLLVMStyleWithColumns(42); + Tab.IndentWidth = 8; + Tab.UseTab = true; + Tab.AlignEscapedNewlinesLeft = true; + verifyFormat("class X {\n" + "\tvoid f() {\n" + "\t\tsomeFunction(parameter1,\n" + "\t\t\t parameter2);\n" + "\t}\n" + "};", + Tab); + verifyFormat("#define A \\\n" + "\tvoid f() { \\\n" + "\t\tsomeFunction( \\\n" + "\t\t parameter1, \\\n" + "\t\t parameter2); \\\n" + "\t}", + Tab); +} + bool allStylesEqual(ArrayRef Styles) { for (size_t i = 1; i < Styles.size(); ++i) if (!(Styles[0] == Styles[i])) @@ -4070,6 +4091,7 @@ TEST_F(FormatTest, ParsesConfiguration) { CHECK_PARSE_BOOL(IndentCaseLabels); CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList); CHECK_PARSE_BOOL(PointerBindsToType); + CHECK_PARSE_BOOL(UseTab); CHECK_PARSE("AccessModifierOffset: -1234", AccessModifierOffset, -1234); CHECK_PARSE("ColumnLimit: 1234", ColumnLimit, 1234u); -- cgit v1.2.3 From af64032b085d3711405dbe8d98f71c463e0fb52b Mon Sep 17 00:00:00 2001 From: Alexander Kornienko Date: Mon, 13 May 2013 12:41:08 +0000 Subject: =?UTF-8?q?Fixes=20[Bug=2015960]=20YAMLTraits=20doesn't=20roundtri?= =?UTF-8?q?p=20on=20Windows.=20Thanks=20to=20Kim=20Gr=C3=A4sman=20for=20he?= =?UTF-8?q?lp!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181699 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 2f2f095780..a62fa379ab 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -191,6 +191,7 @@ std::string configurationAsText(const FormatStyle &Style) { // reference here. FormatStyle NonConstStyle = Style; Output << NonConstStyle; + Stream.flush(); return Text; } -- cgit v1.2.3 From 44135b8836fc3ffb84e540d8a097955271ccbc23 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Mon, 13 May 2013 12:51:40 +0000 Subject: Implements brace breaking styles. We now support "Linux" and "Stroustrup" brace breaking styles, which gets us one step closer to support formatting WebKit, KDE & Linux code. Linux brace breaking style: namespace a { class A { void f() { if (x) { f(); } else { g(); } } } } Stroustrup brace breaking style: namespace a { class A { void f() { if (x) { f(); } else { g(); } } } } git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181700 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Format/Format.h | 17 ++++++++++++++- lib/Format/Format.cpp | 23 +++++++++++++++----- lib/Format/UnwrappedLineParser.cpp | 13 ++++++++++- unittests/Format/FormatTest.cpp | 44 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 7 deletions(-) diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index 79e26d0a0c..47b5bb6907 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -101,6 +101,20 @@ struct FormatStyle { /// tab characters. bool UseTab; + /// \brief Different ways to attach braces to their surrounding context. + enum BraceBreakingStyle { + /// Always attach braces to surrounding context. + BS_Attach, + /// Like \c Attach, but break before braces on function, namespace and + /// class definitions. + BS_Linux, + /// Like \c Attach, but break before function definitions. + BS_Stroustrup + }; + + /// \brief The brace breaking style to use. + BraceBreakingStyle BreakBeforeBraces; + bool operator==(const FormatStyle &R) const { return AccessModifierOffset == R.AccessModifierOffset && AlignEscapedNewlinesLeft == R.AlignEscapedNewlinesLeft && @@ -122,7 +136,8 @@ struct FormatStyle { SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments && Standard == R.Standard && IndentWidth == R.IndentWidth && - UseTab == R.UseTab; + UseTab == R.UseTab && + BreakBeforeBraces == R.BreakBeforeBraces; } }; diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index a62fa379ab..b8c40cf263 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -36,11 +36,21 @@ namespace llvm { namespace yaml { template <> struct ScalarEnumerationTraits { - static void enumeration(IO &io, - clang::format::FormatStyle::LanguageStandard &value) { - io.enumCase(value, "C++03", clang::format::FormatStyle::LS_Cpp03); - io.enumCase(value, "C++11", clang::format::FormatStyle::LS_Cpp11); - io.enumCase(value, "Auto", clang::format::FormatStyle::LS_Auto); + static void enumeration(IO &IO, + clang::format::FormatStyle::LanguageStandard &Value) { + IO.enumCase(Value, "C++03", clang::format::FormatStyle::LS_Cpp03); + IO.enumCase(Value, "C++11", clang::format::FormatStyle::LS_Cpp11); + IO.enumCase(Value, "Auto", clang::format::FormatStyle::LS_Auto); + } +}; + +template<> +struct ScalarEnumerationTraits { + static void + enumeration(IO &IO, clang::format::FormatStyle::BraceBreakingStyle &Value) { + IO.enumCase(Value, "Attach", clang::format::FormatStyle::BS_Attach); + IO.enumCase(Value, "Linux", clang::format::FormatStyle::BS_Linux); + IO.enumCase(Value, "Stroustrup", clang::format::FormatStyle::BS_Stroustrup); } }; @@ -87,6 +97,7 @@ template <> struct MappingTraits { IO.mapOptional("Standard", Style.Standard); IO.mapOptional("IndentWidth", Style.IndentWidth); IO.mapOptional("UseTab", Style.UseTab); + IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces); } }; } @@ -115,6 +126,7 @@ FormatStyle getLLVMStyle() { LLVMStyle.Standard = FormatStyle::LS_Cpp03; LLVMStyle.IndentWidth = 2; LLVMStyle.UseTab = false; + LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach; return LLVMStyle; } @@ -138,6 +150,7 @@ FormatStyle getGoogleStyle() { GoogleStyle.Standard = FormatStyle::LS_Auto; GoogleStyle.IndentWidth = 2; GoogleStyle.UseTab = false; + GoogleStyle.BreakBeforeBraces = FormatStyle::BS_Attach; return GoogleStyle; } diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index 722af5d2b7..a08790d80d 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -402,6 +402,10 @@ void UnwrappedLineParser::parseStructuralElement() { // structural element. // FIXME: Figure out cases where this is not true, and add projections for // them (the one we know is missing are lambdas). + if (Style.BreakBeforeBraces == FormatStyle::BS_Linux || + Style.BreakBeforeBraces == FormatStyle::BS_Stroustrup) + addUnwrappedLine(); + parseBlock(/*MustBeDeclaration=*/ false); addUnwrappedLine(); return; @@ -577,6 +581,9 @@ void UnwrappedLineParser::parseNamespace() { if (FormatTok.Tok.is(tok::identifier)) nextToken(); if (FormatTok.Tok.is(tok::l_brace)) { + if (Style.BreakBeforeBraces == FormatStyle::BS_Linux) + addUnwrappedLine(); + parseBlock(/*MustBeDeclaration=*/ true, 0); // Munch the semicolon after a namespace. This is more common than one would // think. Puttin the semicolon into its own line is very ugly. @@ -751,8 +758,12 @@ void UnwrappedLineParser::parseRecord() { } } } - if (FormatTok.Tok.is(tok::l_brace)) + if (FormatTok.Tok.is(tok::l_brace)) { + if (Style.BreakBeforeBraces == FormatStyle::BS_Linux) + addUnwrappedLine(); + parseBlock(/*MustBeDeclaration=*/ true); + } // We fall through to parsing a structural element afterwards, so // class A {} n, m; // will end up in one unwrapped line. diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index ce79a1c97b..9624ac9224 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -4037,6 +4037,42 @@ TEST_F(FormatTest, ConfigurableUseOfTab) { Tab); } +TEST_F(FormatTest, LinuxBraceBreaking) { + FormatStyle BreakBeforeBrace = getLLVMStyle(); + BreakBeforeBrace.BreakBeforeBraces = FormatStyle::BS_Linux; + verifyFormat("namespace a\n" + "{\n" + "class A\n" + "{\n" + " void f()\n" + " {\n" + " if (true) {\n" + " a();\n" + " b();\n" + " }\n" + " }\n" + "}\n" + "}", + BreakBeforeBrace); +} + +TEST_F(FormatTest, StroustrupBraceBreaking) { + FormatStyle BreakBeforeBrace = getLLVMStyle(); + BreakBeforeBrace.BreakBeforeBraces = FormatStyle::BS_Stroustrup; + verifyFormat("namespace a {\n" + "class A {\n" + " void f()\n" + " {\n" + " if (true) {\n" + " a();\n" + " b();\n" + " }\n" + " }\n" + "}\n" + "}", + BreakBeforeBrace); +} + bool allStylesEqual(ArrayRef Styles) { for (size_t i = 1; i < Styles.size(); ++i) if (!(Styles[0] == Styles[i])) @@ -4113,6 +4149,14 @@ TEST_F(FormatTest, ParsesConfiguration) { CHECK_PARSE("BasedOnStyle: LLVM", ColumnLimit, BaseStyle.ColumnLimit); CHECK_PARSE("BasedOnStyle: LLVM\nColumnLimit: 1234", ColumnLimit, 1234u); + Style.BreakBeforeBraces = FormatStyle::BS_Stroustrup; + CHECK_PARSE("BreakBeforeBraces: Attach", BreakBeforeBraces, + FormatStyle::BS_Attach); + CHECK_PARSE("BreakBeforeBraces: Linux", BreakBeforeBraces, + FormatStyle::BS_Linux); + CHECK_PARSE("BreakBeforeBraces: Stroustrup", BreakBeforeBraces, + FormatStyle::BS_Stroustrup); + #undef CHECK_PARSE #undef CHECK_PARSE_BOOL } -- cgit v1.2.3 From 967d9e935d2379ea00726dc51fac5feeca50a4d0 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Mon, 13 May 2013 12:53:04 +0000 Subject: Fix style according to post-commit review comments. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181701 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/WhitespaceManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Format/WhitespaceManager.cpp b/lib/Format/WhitespaceManager.cpp index 21e38b1e97..7ffebacba5 100644 --- a/lib/Format/WhitespaceManager.cpp +++ b/lib/Format/WhitespaceManager.cpp @@ -143,9 +143,9 @@ std::string WhitespaceManager::getNewLineText(unsigned NewLines, } std::string WhitespaceManager::getIndentText(unsigned Spaces) { - if (!Style.UseTab) { + if (!Style.UseTab) return std::string(Spaces, ' '); - } + return std::string(Spaces / Style.IndentWidth, '\t') + std::string(Spaces % Style.IndentWidth, ' '); } -- cgit v1.2.3 From 2b6acb69f04ac5339fd39a9c03a0f5d076d51ac3 Mon Sep 17 00:00:00 2001 From: Alexander Kornienko Date: Mon, 13 May 2013 12:56:35 +0000 Subject: A better version of r181699: use raw_string_ostream.str() instead of manually calling .flush(). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181702 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index b8c40cf263..384c4628bf 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -204,8 +204,7 @@ std::string configurationAsText(const FormatStyle &Style) { // reference here. FormatStyle NonConstStyle = Style; Output << NonConstStyle; - Stream.flush(); - return Text; + return Stream.str(); } // Returns the length of everything up to the first possible line break after -- cgit v1.2.3 From 77b502580d2d3a04ee0a83e9e22eb6b8d8be3ea6 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Mon, 13 May 2013 14:05:53 +0000 Subject: Fix a gcc warning. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In r181677 I removed this llvm_unreachable and it introduced a gcc warning. Add it back. Thanks to Patrik Hägglund for noticing it. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181704 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ASTContext.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 9901080101..49e3639e93 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -7838,6 +7838,8 @@ GVALinkage ASTContext::GetGVALinkageForVariable(const VarDecl *VD) { case TSK_ImplicitInstantiation: return GVA_TemplateInstantiation; } + + llvm_unreachable("Invalid Linkage!"); } bool ASTContext::DeclMustBeEmitted(const Decl *D) { -- cgit v1.2.3 From 52a4693e1e3943465f52e702e1731340052d5f46 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Mon, 13 May 2013 14:45:11 +0000 Subject: Fix test/CodeGenCXX/captured-statements.cpp on powerpc64 Generalize some attributes that differ on powerpc64 (i32 vs signext i32). Also fix some copy-and-pasted code that didn't get updated properly. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181707 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/CodeGenCXX/captured-statements.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/test/CodeGenCXX/captured-statements.cpp b/test/CodeGenCXX/captured-statements.cpp index 91c7ff28f0..cfa6936111 100644 --- a/test/CodeGenCXX/captured-statements.cpp +++ b/test/CodeGenCXX/captured-statements.cpp @@ -55,9 +55,9 @@ void test2(int x) { }(); // CHECK-2: define void @_Z5test2i - // CHECK-2: call i32 @[[Lambda:["$\w]+]] + // CHECK-2: call {{.*}} @[[Lambda:["$\w]+]] // - // CHECK-2: define internal i32 @[[Lambda]] + // CHECK-2: define internal {{.*}} @[[Lambda]] // CHECK-2: call void @[[HelperName:["$_A-Za-z0-9]+]](%[[Capture:.*]]* // // CHECK-2: define internal void @[[HelperName]] @@ -74,7 +74,7 @@ void test3(int x) { // CHECK-3: %[[Capture:struct\.anon[\.0-9]*]] = type { i32* } - // CHECK-3: define void @_Z5test3i(i32 %x) + // CHECK-3: define void @_Z5test3i // CHECK-3: store i32* // CHECK-3: call void @{{.*}}__captured_stmt // CHECK-3: ret void @@ -86,10 +86,7 @@ void test4() { Foo f; f.x = 5; } - // CHECK-4: %[[Capture:struct\.anon[\.0-9]*]] = type { i32* } - - // CHECK-4: define void @_Z5test3i(i32 %x) - // CHECK-4: store i32* + // CHECK-4: define void @_Z5test4v // CHECK-4: call void @[[HelperName:["$_A-Za-z0-9]+]](%[[Capture:.*]]* // CHECK-4: ret void // -- cgit v1.2.3 From fbff0c4510c7f0e0f30a005960e434b973f5bd21 Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Mon, 13 May 2013 17:27:00 +0000 Subject: Objective-C error recovery. This patch makes a quick recovery form duplicate method definition error thus preventing doc parsing to loop trying to find comment for the invalid redefinition in a previous declaration. // rdar://13836387 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181710 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ASTContext.cpp | 2 ++ lib/Sema/SemaDeclObjC.cpp | 2 ++ test/Sema/warn-documentation.m | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 49e3639e93..21a16a6d98 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -409,6 +409,8 @@ comments::FullComment *ASTContext::cloneFullComment(comments::FullComment *FC, comments::FullComment *ASTContext::getCommentForDecl( const Decl *D, const Preprocessor *PP) const { + if (D->isInvalidDecl()) + return NULL; D = adjustDeclToTemplate(D); const Decl *Canonical = D->getCanonicalDecl(); diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index f33e7bcb16..07610585c5 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3052,6 +3052,8 @@ Decl *Sema::ActOnMethodDeclaration( Diag(ObjCMethod->getLocation(), diag::err_duplicate_method_decl) << ObjCMethod->getDeclName(); Diag(PrevMethod->getLocation(), diag::note_previous_declaration); + ObjCMethod->setInvalidDecl(); + return ObjCMethod; } // If this Objective-C method does not have a related result type, but we diff --git a/test/Sema/warn-documentation.m b/test/Sema/warn-documentation.m index 14e0c347e3..2720480509 100644 --- a/test/Sema/warn-documentation.m +++ b/test/Sema/warn-documentation.m @@ -176,3 +176,24 @@ struct S; // expected-warning@+1 {{unknown command tag name}} /// \t bbb IS_DOXYGEN_END int FooBar(); + +// rdar://13836387 +/** \brief Module handling the incoming notifications from the system. + * + * This includes: + * - Network Reachability + * - Power State + * - Low Disk + */ +@interface BRC : NSObject +- (void)removeReach:(NSObject*)observer; +@end + +@implementation BRC : NSObject +- (void)removeReach:(NSObject*)observer // expected-note {{previous declaration is here}} +{ +} +- (void)removeReach:(NSObject*)observer // expected-error {{duplicate declaration of method 'removeReach:'}} +{ +} +@end -- cgit v1.2.3 From 620c0afe5ea7185688d066de9e3b3680067934d3 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Mon, 13 May 2013 20:09:47 +0000 Subject: Use atomic instructions on ARM linux. This is safe given how the pre-v6 atomic ops funcions in libgcc are implemented. This fixes pr15429. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181728 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Basic/Targets.cpp | 17 ++++++++++++++++- test/CodeGen/linux-arm-atomic.c | 10 ++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 test/CodeGen/linux-arm-atomic.c diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 96b12bac1f..841ca62ab6 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -3527,6 +3527,20 @@ class ARMTargetInfo : public TargetInfo { static const Builtin::Info BuiltinInfo[]; + static bool shouldUseInlineAtomic(const llvm::Triple &T) { + // On linux, binaries targeting old cpus call functions in libgcc to + // perform atomic operations. The implementation in libgcc then calls into + // the kernel which on armv6 and newer uses ldrex and strex. The net result + // is that if we assume the kernel is at least as recent as the hardware, + // it is safe to use atomic instructions on armv6 and newer. + if (T.getOS() != llvm::Triple::Linux) + return false; + StringRef ArchName = T.getArchName(); + if (ArchName.startswith("armv6") || ArchName.startswith("armv7")) + return true; + return false; + } + public: ARMTargetInfo(const std::string &TripleStr) : TargetInfo(TripleStr), ABI("aapcs-linux"), CPU("arm1136j-s"), IsAAPCS(true) @@ -3559,8 +3573,9 @@ public: TheCXXABI.set(TargetCXXABI::GenericARM); // ARM has atomics up to 8 bytes - // FIXME: Set MaxAtomicInlineWidth if we have the feature v6e MaxAtomicPromoteWidth = 64; + if (shouldUseInlineAtomic(getTriple())) + MaxAtomicInlineWidth = 64; // Do force alignment of members that follow zero length bitfields. If // the alignment of the zero-length bitfield is greater than the member diff --git a/test/CodeGen/linux-arm-atomic.c b/test/CodeGen/linux-arm-atomic.c new file mode 100644 index 0000000000..3fda7f688c --- /dev/null +++ b/test/CodeGen/linux-arm-atomic.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=armv7-unknown-linux | FileCheck %s +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=armv6-unknown-linux | FileCheck %s + +typedef int _Atomic_word; +_Atomic_word exchange_and_add(volatile _Atomic_word *__mem, int __val) { + return __atomic_fetch_add(__mem, __val, __ATOMIC_ACQ_REL); +} + +// CHECK: define {{.*}} @exchange_and_add +// CHECK: atomicrmw {{.*}} add -- cgit v1.2.3 From 7e17fdc617ee9db8270f3f6fb4ecd392fed47d80 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 13 May 2013 20:28:15 +0000 Subject: Fix a wrong and confusing comment in CharUnits.h. Neither C nor C++ allows bytes and character units to be different sizes. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181730 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/CharUnits.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/include/clang/AST/CharUnits.h b/include/clang/AST/CharUnits.h index 082c672c21..09ff6828ef 100644 --- a/include/clang/AST/CharUnits.h +++ b/include/clang/AST/CharUnits.h @@ -19,21 +19,20 @@ #include "llvm/Support/MathExtras.h" namespace clang { - + /// CharUnits - This is an opaque type for sizes expressed in character units. - /// Instances of this type represent a quantity as a multiple of the size + /// Instances of this type represent a quantity as a multiple of the size /// of the standard C type, char, on the target architecture. As an opaque /// type, CharUnits protects you from accidentally combining operations on - /// quantities in bit units and character units. + /// quantities in bit units and character units. + /// + /// In both C and C++, an object of type 'char', 'signed char', or 'unsigned + /// char' occupies exactly one byte, so 'character unit' and 'byte' refer to + /// the same quantity of storage. However, we use the term 'character unit' + /// rather than 'byte' to avoid an implication that a character unit is + /// exactly 8 bits. /// - /// It should be noted that characters and bytes are distinct concepts. Bytes - /// refer to addressable units of data storage on the target machine, and - /// characters are members of a set of elements used for the organization, - /// control, or representation of data. According to C99, bytes are allowed - /// to exceed characters in size, although currently, clang only supports - /// architectures where the two are the same size. - /// - /// For portability, never assume that a target character is 8 bits wide. Use + /// For portability, never assume that a target character is 8 bits wide. Use /// CharUnit values wherever you calculate sizes, offsets, or alignments /// in character units. class CharUnits { -- cgit v1.2.3 From 1071b9f2d38f8177e54f20412a36450462c19186 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 13 May 2013 20:33:30 +0000 Subject: Suppress GCC warning for no return after covered switch, and remove some debugging code from an unreachable codepath. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181731 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 339e78bbe9..0f44985471 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -2869,10 +2869,9 @@ static EvalStmtResult EvaluateSwitch(APValue &Result, EvalInfo &Info, case ESR_Returned: return ESR; case ESR_CaseNotFound: - Found->dump(); - SS->getBody()->dump(); llvm_unreachable("couldn't find switch case"); } + llvm_unreachable("Invalid EvalStmtResult!"); } // Evaluate a statement. -- cgit v1.2.3 From 27c7f54cf7d18276be2979d5c795533cc5592675 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 13 May 2013 20:50:15 +0000 Subject: Align a multiline string literal with the first part. Before: #define A(X) \ "aaaaa" #X "bbbbbb" \ "ccccc" After: #define A(X) \ "aaaaa" #X "bbbbbb" \ "ccccc" git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181732 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 6 +++--- unittests/Format/FormatTest.cpp | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 384c4628bf..50567a6f84 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -738,10 +738,10 @@ private: State.Stack.back().VariablePos = VariablePos; } - if (Current.is(tok::string_literal)) { + if (Current.is(tok::string_literal) && State.StartOfStringLiteral == 0) { State.StartOfStringLiteral = State.Column; - } else if (Current.isNot(tok::comment)) { - State.StartOfStringLiteral = 0; + } else if (!Current.isOneOf(tok::comment, tok::identifier, tok::hash, + tok::string_literal)) { } State.Column += Current.FormatTok.TokenLength; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 9624ac9224..697cc53820 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -2231,6 +2231,14 @@ TEST_F(FormatTest, AlignsStringLiterals) { "#define LL_FORMAT \"ll\"\n" "printf(\"aaaaa: %d, bbbbbb: %\" LL_FORMAT \"d, cccccccc: %\" LL_FORMAT\n" " \"d, ddddddddd: %\" LL_FORMAT \"d\");"); + + verifyFormat("#define A(X) \\\n" + " \"aaaaa\" #X \"bbbbbb\" \\\n" + " \"ccccc\"", + getLLVMStyleWithColumns(23)); + verifyFormat("#define A \"def\"\n" + "f(\"abc\" A \"ghi\"\n" + " \"jkl\");"); } TEST_F(FormatTest, AlignsPipes) { -- cgit v1.2.3 From b834a78f9b79cb71b093ebbbb381b92f9d4bbf3b Mon Sep 17 00:00:00 2001 From: Anna Zaks Date: Mon, 13 May 2013 21:48:20 +0000 Subject: [analyzer] Warn about nil elements/keys/values in array and dictionary literals. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181738 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Checkers/BasicObjCFoundationChecks.cpp | 95 +++++++++++++++++----- test/Analysis/NSContainers.m | 33 ++++++++ 2 files changed, 109 insertions(+), 19 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 6388a8df64..c723e4f29b 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -90,20 +90,53 @@ static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) { //===----------------------------------------------------------------------===// namespace { - class NilArgChecker : public Checker { + class NilArgChecker : public Checker, + check::PostStmt > { mutable OwningPtr BT; - void WarnIfNilArg(CheckerContext &C, - const ObjCMethodCall &msg, unsigned Arg, - FoundationClass Class, - bool CanBeSubscript = false) const; + void warnIfNilExpr(const Expr *E, + const char *Msg, + CheckerContext &C) const; + + void warnIfNilArg(CheckerContext &C, + const ObjCMethodCall &msg, unsigned Arg, + FoundationClass Class, + bool CanBeSubscript = false) const; + + void generateBugReport(ExplodedNode *N, + llvm::raw_svector_ostream &os, + SourceRange Range, + const Expr *Expr, + CheckerContext &C) const; public: void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPostStmt(const ObjCDictionaryLiteral *DL, + CheckerContext &C) const; + void checkPostStmt(const ObjCArrayLiteral *AL, + CheckerContext &C) const; }; } -void NilArgChecker::WarnIfNilArg(CheckerContext &C, +void NilArgChecker::warnIfNilExpr(const Expr *E, + const char *Msg, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal SV = State->getSVal(E, C.getLocationContext()); + if (State->isNull(SV).isConstrainedTrue()) { + + if (ExplodedNode *N = C.generateSink()) { + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << Msg; + generateBugReport(N, os, E->getSourceRange(), E, C); + } + + } +} + +void NilArgChecker::warnIfNilArg(CheckerContext &C, const ObjCMethodCall &msg, unsigned int Arg, FoundationClass Class, @@ -113,9 +146,6 @@ void NilArgChecker::WarnIfNilArg(CheckerContext &C, if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) return; - if (!BT) - BT.reset(new APIMisuse("nil argument")); - if (ExplodedNode *N = C.generateSink()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); @@ -149,14 +179,26 @@ void NilArgChecker::WarnIfNilArg(CheckerContext &C, << msg.getSelector().getAsString() << "' cannot be nil"; } } - - BugReport *R = new BugReport(*BT, os.str(), N); - R->addRange(msg.getArgSourceRange(Arg)); - bugreporter::trackNullOrUndefValue(N, msg.getArgExpr(Arg), *R); - C.emitReport(R); + + generateBugReport(N, os, msg.getArgSourceRange(Arg), + msg.getArgExpr(Arg), C); } } +void NilArgChecker::generateBugReport(ExplodedNode *N, + llvm::raw_svector_ostream &os, + SourceRange Range, + const Expr *Expr, + CheckerContext &C) const { + if (!BT) + BT.reset(new APIMisuse("nil argument")); + + BugReport *R = new BugReport(*BT, os.str(), N); + R->addRange(Range); + bugreporter::trackNullOrUndefValue(N, Expr, *R); + C.emitReport(R); +} + void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const { const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); @@ -225,28 +267,43 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, if (S.getNameForSlot(0).equals("dictionaryWithObject") && S.getNameForSlot(1).equals("forKey")) { Arg = 0; - WarnIfNilArg(C, msg, /* Arg */1, Class); + warnIfNilArg(C, msg, /* Arg */1, Class); } else if (S.getNameForSlot(0).equals("setObject") && S.getNameForSlot(1).equals("forKey")) { Arg = 0; - WarnIfNilArg(C, msg, /* Arg */1, Class); + warnIfNilArg(C, msg, /* Arg */1, Class); } else if (S.getNameForSlot(0).equals("setObject") && S.getNameForSlot(1).equals("forKeyedSubscript")) { CanBeSubscript = true; Arg = 0; - WarnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript); + warnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript); } else if (S.getNameForSlot(0).equals("removeObjectForKey")) { Arg = 0; } } - // If argument is '0', report a warning. if ((Arg != InvalidArgIndex)) - WarnIfNilArg(C, msg, Arg, Class, CanBeSubscript); + warnIfNilArg(C, msg, Arg, Class, CanBeSubscript); } +void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL, + CheckerContext &C) const { + for (unsigned i = 0; i < AL->getNumElements(); ++i) { + warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C); + } +} + +void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, + CheckerContext &C) const { + for (unsigned i = 0; i < DL->getNumElements(); ++i) { + ObjCDictionaryElement Element = DL->getKeyValueElement(i); + warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C); + warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C); + } +} + //===----------------------------------------------------------------------===// // Error reporting. //===----------------------------------------------------------------------===// diff --git a/test/Analysis/NSContainers.m b/test/Analysis/NSContainers.m index 6b4089b3e5..959f367d28 100644 --- a/test/Analysis/NSContainers.m +++ b/test/Analysis/NSContainers.m @@ -36,6 +36,10 @@ typedef struct _NSZone NSZone; - (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx __attribute__((availability(macosx,introduced=10.8))); @end +@interface NSArray (NSArrayCreation) ++ (instancetype)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt; +@end + @interface NSMutableArray : NSArray - (void)addObject:(id)anObject; @@ -58,6 +62,8 @@ typedef struct _NSZone NSZone; + (id)dictionary; + (id)dictionaryWithObject:(id)object forKey:(id )key; ++ (instancetype)dictionaryWithObjects:(const id [])objects forKeys:(const id [])keys count:(NSUInteger)cnt; + @end @interface NSMutableDictionary : NSDictionary @@ -147,6 +153,33 @@ NSDictionary *testNilArgNSDictionary2(NSObject *obj) { return [NSDictionary dictionaryWithObject:obj forKey:0]; // expected-warning {{Key argument to 'dictionaryWithObject:forKey:' cannot be nil}} } +id testCreateDictionaryLiteralKey(id value, id nilKey) { + if (nilKey) + ; + return @{@"abc":value, nilKey:@"abc"}; // expected-warning {{Dictionary key cannot be nil}} +} + +id testCreateDictionaryLiteralValue(id nilValue) { + if (nilValue) + ; + return @{@"abc":nilValue}; // expected-warning {{Dictionary value cannot be nil}} +} + +id testCreateDictionaryLiteral(id nilValue, id nilKey) { + if (nilValue) + ; + if (nilKey) + ; + return @{@"abc":nilValue, nilKey:@"abc"}; // expected-warning {{Dictionary key cannot be nil}} + // expected-warning@-1 {{Dictionary value cannot be nil}} +} + +id testCreateArrayLiteral(id myNil) { + if (myNil) + ; + return @[ @"a", myNil, @"c" ]; // expected-warning {{Array element cannot be nil}} +} + // Test inline defensive checks suppression. void idc(id x) { if (x) -- cgit v1.2.3 From ef202c35b37c137e32fe30f4453915b6d3b525d7 Mon Sep 17 00:00:00 2001 From: Anna Zaks Date: Mon, 13 May 2013 23:49:51 +0000 Subject: =?UTF-8?q?[analyzer]=20Refactor:=20address=20Jordan=E2=80=99s=20c?= =?UTF-8?q?ode=20review=20of=20r181738.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (Modifying the checker to record that the values are no longer nil will be done separately.) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181744 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Checkers/BasicObjCFoundationChecks.cpp | 26 +- test/Analysis/inlining/path-notes.m | 492 +++++++++++++++------ 2 files changed, 375 insertions(+), 143 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index c723e4f29b..ba779ff191 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -105,7 +105,7 @@ namespace { bool CanBeSubscript = false) const; void generateBugReport(ExplodedNode *N, - llvm::raw_svector_ostream &os, + StringRef Msg, SourceRange Range, const Expr *Expr, CheckerContext &C) const; @@ -123,14 +123,10 @@ void NilArgChecker::warnIfNilExpr(const Expr *E, const char *Msg, CheckerContext &C) const { ProgramStateRef State = C.getState(); - SVal SV = State->getSVal(E, C.getLocationContext()); - if (State->isNull(SV).isConstrainedTrue()) { + if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { if (ExplodedNode *N = C.generateSink()) { - SmallString<128> sbuf; - llvm::raw_svector_ostream os(sbuf); - os << Msg; - generateBugReport(N, os, E->getSourceRange(), E, C); + generateBugReport(N, Msg, E->getSourceRange(), E, C); } } @@ -180,22 +176,22 @@ void NilArgChecker::warnIfNilArg(CheckerContext &C, } } - generateBugReport(N, os, msg.getArgSourceRange(Arg), + generateBugReport(N, os.str(), msg.getArgSourceRange(Arg), msg.getArgExpr(Arg), C); } } void NilArgChecker::generateBugReport(ExplodedNode *N, - llvm::raw_svector_ostream &os, + StringRef Msg, SourceRange Range, - const Expr *Expr, + const Expr *E, CheckerContext &C) const { if (!BT) BT.reset(new APIMisuse("nil argument")); - BugReport *R = new BugReport(*BT, os.str(), N); + BugReport *R = new BugReport(*BT, Msg, N); R->addRange(Range); - bugreporter::trackNullOrUndefValue(N, Expr, *R); + bugreporter::trackNullOrUndefValue(N, E, *R); C.emitReport(R); } @@ -290,14 +286,16 @@ void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const { - for (unsigned i = 0; i < AL->getNumElements(); ++i) { + unsigned NumOfElements = AL->getNumElements(); + for (unsigned i = 0; i < NumOfElements; ++i) { warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C); } } void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const { - for (unsigned i = 0; i < DL->getNumElements(); ++i) { + unsigned NumOfElements = DL->getNumElements(); + for (unsigned i = 0; i < NumOfElements; ++i) { ObjCDictionaryElement Element = DL->getKeyValueElement(i); warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C); warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C); diff --git a/test/Analysis/inlining/path-notes.m b/test/Analysis/inlining/path-notes.m index 74f088a382..602bad188a 100644 --- a/test/Analysis/inlining/path-notes.m +++ b/test/Analysis/inlining/path-notes.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-output=text -analyzer-config suppress-null-return-paths=false -fblocks -verify %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-output=plist-multi-file -analyzer-config suppress-null-return-paths=false -fblocks %s -o %t.plist +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.NilArg -analyzer-output=text -analyzer-config suppress-null-return-paths=false -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.NilArg -analyzer-output=plist-multi-file -analyzer-config suppress-null-return-paths=false -fblocks %s -o %t.plist // RUN: FileCheck --input-file=%t.plist %s typedef struct dispatch_queue_s *dispatch_queue_t; @@ -11,6 +11,57 @@ void dispatch_sync(dispatch_queue_t, dispatch_block_t); @property int *p; @end +typedef unsigned long NSUInteger; +typedef signed char BOOL; +typedef struct _NSZone NSZone; +@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; +@protocol NSObject +@end +@protocol NSCopying +- (id)copyWithZone:(NSZone *)zone; +@end +@protocol NSMutableCopying +- (id)mutableCopyWithZone:(NSZone *)zone; +@end +@protocol NSCoding +- (void)encodeWithCoder:(NSCoder *)aCoder; +@end +@protocol NSFastEnumeration +@end +@protocol NSSecureCoding +@required ++ (BOOL)supportsSecureCoding; +@end +@interface NSObject {} +- (id)init; ++ (id)alloc; +@end +@interface NSArray : NSObject + +- (NSUInteger)count; +- (id)objectAtIndex:(NSUInteger)index; + +@end + +@interface NSArray (NSExtendedArray) +- (NSArray *)arrayByAddingObject:(id)anObject; +- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx __attribute__((availability(macosx,introduced=10.8))); +@end + +@interface NSArray (NSArrayCreation) ++ (instancetype)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt; +@end + +@interface NSMutableArray : NSArray + +- (void)addObject:(id)anObject; +- (void)insertObject:(id)anObject atIndex:(NSUInteger)index; +- (void)removeLastObject; +- (void)removeObjectAtIndex:(NSUInteger)index; +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject; + +@end + int *getZeroIfNil(Test *x) { return x.p; // expected-note@-1 {{'p' not called because the receiver is nil}} @@ -89,6 +140,12 @@ void testNilReceiver(id *x) { // expected-note@-3 {{Calling 'testNilReceiverHelper'}} } +id testCreateArrayLiteral(id myNil) { + if (myNil) // expected-note {{Assuming 'myNil' is nil}} + ; // expected-note@-1 {{Taking false branch}} + return @[ @"a", myNil, @"c" ]; // expected-warning {{Array element cannot be nil}} + //expected-note@-1 {{Array element cannot be nil}} +} // CHECK: diagnostics // CHECK-NEXT: @@ -103,12 +160,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -116,12 +173,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col17 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col17 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -133,7 +190,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col17 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -141,12 +198,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col17 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col17 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -166,12 +223,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col17 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col17 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -179,12 +236,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col15 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -196,7 +253,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -204,12 +261,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -225,7 +282,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line14 +// CHECK-NEXT: line65 // CHECK-NEXT: col1 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -243,12 +300,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line14 +// CHECK-NEXT: line65 // CHECK-NEXT: col1 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line14 +// CHECK-NEXT: line65 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -256,12 +313,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -277,12 +334,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -290,12 +347,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -307,7 +364,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -315,12 +372,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -340,12 +397,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -353,12 +410,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -370,7 +427,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -378,12 +435,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line15 +// CHECK-NEXT: line66 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -399,7 +456,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -407,12 +464,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col18 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -432,12 +489,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col15 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -445,12 +502,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col20 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col20 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -462,7 +519,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col20 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -470,12 +527,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col22 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -496,7 +553,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line21 +// CHECK-NEXT: line72 // CHECK-NEXT: col20 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -512,12 +569,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line30 +// CHECK-NEXT: line81 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line30 +// CHECK-NEXT: line81 // CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -525,12 +582,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col15 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -542,7 +599,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -550,12 +607,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line41 +// CHECK-NEXT: line92 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -599,7 +656,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col30 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -617,12 +674,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col30 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col30 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -630,12 +687,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line89 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line89 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -647,7 +704,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line89 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -655,12 +712,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line89 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line38 +// CHECK-NEXT: line89 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -690,7 +747,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -698,12 +755,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line41 +// CHECK-NEXT: line92 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -723,12 +780,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line36 +// CHECK-NEXT: line87 // CHECK-NEXT: col15 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -736,12 +793,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line94 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line94 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -753,7 +810,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line94 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -761,12 +818,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line94 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line94 // CHECK-NEXT: col14 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -787,7 +844,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: issue_hash14 // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line43 +// CHECK-NEXT: line94 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -803,12 +860,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line51 +// CHECK-NEXT: line102 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line51 +// CHECK-NEXT: line102 // CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -816,12 +873,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line56 +// CHECK-NEXT: line107 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line56 +// CHECK-NEXT: line107 // CHECK-NEXT: col15 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -833,7 +890,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line56 +// CHECK-NEXT: line107 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -841,12 +898,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line56 +// CHECK-NEXT: line107 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line62 +// CHECK-NEXT: line113 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -890,7 +947,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line56 +// CHECK-NEXT: line107 // CHECK-NEXT: col30 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -908,12 +965,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line56 +// CHECK-NEXT: line107 // CHECK-NEXT: col30 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line56 +// CHECK-NEXT: line107 // CHECK-NEXT: col30 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -921,12 +978,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line109 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line109 // CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -938,7 +995,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line109 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -946,12 +1003,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line109 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line109 // CHECK-NEXT: col9 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -971,12 +1028,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line109 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line58 +// CHECK-NEXT: line109 // CHECK-NEXT: col7 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -984,12 +1041,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line60 +// CHECK-NEXT: line111 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line60 +// CHECK-NEXT: line111 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1001,7 +1058,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line60 +// CHECK-NEXT: line111 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1009,12 +1066,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line60 +// CHECK-NEXT: line111 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line60 +// CHECK-NEXT: line111 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1032,7 +1089,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: typeuninitialized variable captured by block // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line60 +// CHECK-NEXT: line111 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1048,12 +1105,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line82 +// CHECK-NEXT: line133 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line82 +// CHECK-NEXT: line133 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1061,12 +1118,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col23 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1082,12 +1139,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col23 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1095,12 +1152,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col26 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col26 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1112,7 +1169,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col26 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1120,12 +1177,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col26 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col27 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1145,12 +1202,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col26 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col26 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1158,12 +1215,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col25 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col25 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1175,7 +1232,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col25 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1183,12 +1240,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col25 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col35 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1204,7 +1261,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1212,12 +1269,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line86 +// CHECK-NEXT: line137 // CHECK-NEXT: col36 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1233,7 +1290,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line76 +// CHECK-NEXT: line127 // CHECK-NEXT: col1 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1251,12 +1308,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line76 +// CHECK-NEXT: line127 // CHECK-NEXT: col1 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line76 +// CHECK-NEXT: line127 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1264,12 +1321,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line77 +// CHECK-NEXT: line128 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line77 +// CHECK-NEXT: line128 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1285,12 +1342,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line77 +// CHECK-NEXT: line128 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line77 +// CHECK-NEXT: line128 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1298,12 +1355,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line77 +// CHECK-NEXT: line128 // CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line77 +// CHECK-NEXT: line128 // CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1315,7 +1372,7 @@ void testNilReceiver(id *x) { // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line77 +// CHECK-NEXT: line128 // CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1323,12 +1380,12 @@ void testNilReceiver(id *x) { // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line77 +// CHECK-NEXT: line128 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line77 +// CHECK-NEXT: line128 // CHECK-NEXT: col4 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -1349,9 +1406,186 @@ void testNilReceiver(id *x) { // CHECK-NEXT: issue_hash1 // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line77 +// CHECK-NEXT: line128 // CHECK-NEXT: col6 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: path +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line144 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line144 +// CHECK-NEXT: col4 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line144 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line144 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line144 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line144 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line144 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Assuming 'myNil' is nil +// CHECK-NEXT: message +// CHECK-NEXT: Assuming 'myNil' is nil +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line144 +// CHECK-NEXT: col7 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line144 +// CHECK-NEXT: col11 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line146 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line146 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line146 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line146 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line146 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line146 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line146 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line146 +// CHECK-NEXT: col19 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line146 +// CHECK-NEXT: col23 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Array element cannot be nil +// CHECK-NEXT: message +// CHECK-NEXT: Array element cannot be nil +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: descriptionArray element cannot be nil +// CHECK-NEXT: categoryAPI Misuse (Apple) +// CHECK-NEXT: typenil argument +// CHECK-NEXT: issue_context_kindfunction +// CHECK-NEXT: issue_contexttestCreateArrayLiteral +// CHECK-NEXT: issue_hash3 +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line146 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: -- cgit v1.2.3 From 7be62a87abf60d4c13ab60ee8fb896aebac188a8 Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Tue, 14 May 2013 00:34:20 +0000 Subject: PR15956: Debug Info: Include the appropriate file location in types created due to using declarations We might benefit from API refactoring here (why pass in a value that's derived from another parameter?) but this is the immediate issue. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181747 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGDebugInfo.cpp | 6 ++++-- test/CodeGenCXX/debug-info-namespace.cpp | 27 ++++++++++++++++----------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 583b23980e..f6ad8f2cc5 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -2178,8 +2178,10 @@ llvm::DIDescriptor CGDebugInfo::getDeclarationOrDefinition(const Decl *D) { // we would otherwise do to get a type for a pointee. (forward declarations in // limited debug info, full definitions (if the type definition is available) // in unlimited debug info) - if (const TypeDecl *RD = dyn_cast(D)) - return CreatePointeeType(QualType(RD->getTypeForDecl(), 0), llvm::DIFile()); + if (const TypeDecl *TD = dyn_cast(D)) { + llvm::DIFile DefUnit = getOrCreateFile(TD->getLocation()); + return CreatePointeeType(CGM.getContext().getTypeDeclType(TD), DefUnit); + } // Otherwise fall back to a fairly rudimentary cache of existing declarations. // This doesn't handle providing declarations (for functions or variables) for // entities without definitions in this TU, nor when the definition proceeds diff --git a/test/CodeGenCXX/debug-info-namespace.cpp b/test/CodeGenCXX/debug-info-namespace.cpp index 4b44661eb1..9eff818f87 100644 --- a/test/CodeGenCXX/debug-info-namespace.cpp +++ b/test/CodeGenCXX/debug-info-namespace.cpp @@ -8,6 +8,7 @@ void f1() { } void f1(int) { } struct foo; struct bar { }; +typedef bar baz; } using namespace B; } @@ -24,6 +25,7 @@ int func(bool b) { using B::bar; using B::f1; using B::i; + using B::baz; bar x; return i; } @@ -33,24 +35,27 @@ int func(bool b) { // CHECK: [[CU:![0-9]*]] = {{.*}}[[MODULES:![0-9]*]], metadata !""} ; [ DW_TAG_compile_unit ] // CHECK: [[FILE:![0-9]*]] {{.*}}debug-info-namespace.cpp" +// CHECK: [[FOOCPP:![0-9]*]] = metadata !{metadata !"foo.cpp", {{.*}} // CHECK: [[NS:![0-9]*]] = {{.*}}, metadata [[FILE2:![0-9]*]], metadata [[CTXT:![0-9]*]], {{.*}} ; [ DW_TAG_namespace ] [B] [line 1] // CHECK: [[CTXT]] = {{.*}}, metadata [[FILE]], null, {{.*}} ; [ DW_TAG_namespace ] [A] [line 3] // CHECK: [[F1:![0-9]*]] {{.*}} ; [ DW_TAG_subprogram ] [line 4] [def] [f1] -// CHECK: [[FUNC:![0-9]*]] {{.*}} ; [ DW_TAG_subprogram ] [line 13] [def] [func] +// CHECK: [[FUNC:![0-9]*]] {{.*}} ; [ DW_TAG_subprogram ] [line 14] [def] [func] // CHECK: [[FILE2]]} ; [ DW_TAG_file_type ] [{{.*}}foo.cpp] // CHECK: [[I:![0-9]*]] = {{.*}}, metadata [[NS]], metadata !"i", {{.*}} ; [ DW_TAG_variable ] [i] -// CHECK: [[MODULES]] = metadata !{metadata [[M1:![0-9]*]], metadata [[M2:![0-9]*]], metadata [[M3:![0-9]*]], metadata [[M4:![0-9]*]], metadata [[M5:![0-9]*]], metadata [[M6:![0-9]*]], metadata [[M7:![0-9]*]], metadata [[M8:![0-9]*]]} -// CHECK: [[M1]] = metadata !{i32 {{[0-9]*}}, metadata [[CTXT]], metadata [[NS]], i32 8} ; [ DW_TAG_imported_module ] -// CHECK: [[M2]] = metadata !{i32 {{[0-9]*}}, metadata [[CU]], metadata [[CTXT]], i32 11} ; [ DW_TAG_imported_module ] -// CHECK: [[M3]] = metadata !{i32 {{[0-9]*}}, metadata [[LEX:![0-9]*]], metadata [[NS]], i32 15} ; [ DW_TAG_imported_module ] -// CHECK: [[LEX]] = metadata !{i32 {{[0-9]*}}, metadata [[FILE2]], metadata [[FUNC]], i32 14, i32 0, i32 0} ; [ DW_TAG_lexical_block ] -// CHECK: [[M4]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[CTXT]], i32 18} ; [ DW_TAG_imported_module ] -// CHECK: [[M5]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[FOO:![0-9]*]], i32 19} ; [ DW_TAG_imported_declaration ] +// CHECK: [[MODULES]] = metadata !{metadata [[M1:![0-9]*]], metadata [[M2:![0-9]*]], metadata [[M3:![0-9]*]], metadata [[M4:![0-9]*]], metadata [[M5:![0-9]*]], metadata [[M6:![0-9]*]], metadata [[M7:![0-9]*]], metadata [[M8:![0-9]*]], metadata [[M9:![0-9]*]]} +// CHECK: [[M1]] = metadata !{i32 {{[0-9]*}}, metadata [[CTXT]], metadata [[NS]], i32 9} ; [ DW_TAG_imported_module ] +// CHECK: [[M2]] = metadata !{i32 {{[0-9]*}}, metadata [[CU]], metadata [[CTXT]], i32 12} ; [ DW_TAG_imported_module ] +// CHECK: [[M3]] = metadata !{i32 {{[0-9]*}}, metadata [[LEX:![0-9]*]], metadata [[NS]], i32 16} ; [ DW_TAG_imported_module ] +// CHECK: [[LEX]] = metadata !{i32 {{[0-9]*}}, metadata [[FILE2]], metadata [[FUNC]], i32 15, i32 0, i32 0} ; [ DW_TAG_lexical_block ] +// CHECK: [[M4]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[CTXT]], i32 19} ; [ DW_TAG_imported_module ] +// CHECK: [[M5]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[FOO:![0-9]*]], i32 20} ; [ DW_TAG_imported_declaration ] // CHECK: [[FOO]] {{.*}} ; [ DW_TAG_structure_type ] [foo] [line 5, size 0, align 0, offset 0] [fwd] [from ] -// CHECK: [[M6]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[BAR:![0-9]*]], i32 20} ; [ DW_TAG_imported_declaration ] +// CHECK: [[M6]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[BAR:![0-9]*]], i32 21} ; [ DW_TAG_imported_declaration ] // CHECK: [[BAR]] {{.*}} ; [ DW_TAG_structure_type ] [bar] [line 6, {{.*}}] [from ] -// CHECK: [[M7]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[F1]], i32 21} ; [ DW_TAG_imported_declaration ] -// CHECK: [[M8]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[I]], i32 22} ; [ DW_TAG_imported_declaration ] +// CHECK: [[M7]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[F1]], i32 22} ; [ DW_TAG_imported_declaration ] +// CHECK: [[M8]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[I]], i32 23} ; [ DW_TAG_imported_declaration ] +// CHECK: [[M9]] = metadata !{i32 {{[0-9]*}}, metadata [[FUNC]], metadata [[BAZ:![0-9]*]], i32 24} ; [ DW_TAG_imported_declaration ] +// CHECK: [[BAZ]] = metadata !{i32 {{[0-9]*}}, metadata [[FOOCPP]], metadata [[NS]], {{.*}} ; [ DW_TAG_typedef ] [baz] {{.*}} [from bar] // FIXME: It is confused on win32 to generate file entry when dosish filename is given. // REQUIRES: shell -- cgit v1.2.3 From 69db555a7a4d0aa11d65001ffc25669c354a5de0 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Tue, 14 May 2013 00:44:24 +0000 Subject: Use atomic instructions on linux thumb v7. This matches gcc's behaviour. The patch also explicitly parses the version so that this keeps working when we add support for v8. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181750 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Basic/Targets.cpp | 20 +++++++++++++++++--- test/CodeGen/linux-arm-atomic.c | 1 + 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 841ca62ab6..a622a11aa5 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -3536,9 +3536,23 @@ class ARMTargetInfo : public TargetInfo { if (T.getOS() != llvm::Triple::Linux) return false; StringRef ArchName = T.getArchName(); - if (ArchName.startswith("armv6") || ArchName.startswith("armv7")) - return true; - return false; + if (T.getArch() == llvm::Triple::arm) { + if (!ArchName.startswith("armv")) + return false; + StringRef VersionStr = ArchName.substr(4); + unsigned Version; + if (VersionStr.getAsInteger(10, Version)) + return false; + return Version >= 6; + } + assert(T.getArch() == llvm::Triple::thumb); + if (!ArchName.startswith("thumbv")) + return false; + StringRef VersionStr = ArchName.substr(6); + unsigned Version; + if (VersionStr.getAsInteger(10, Version)) + return false; + return Version >= 7; } public: diff --git a/test/CodeGen/linux-arm-atomic.c b/test/CodeGen/linux-arm-atomic.c index 3fda7f688c..c7ce1d228b 100644 --- a/test/CodeGen/linux-arm-atomic.c +++ b/test/CodeGen/linux-arm-atomic.c @@ -1,5 +1,6 @@ // RUN: %clang_cc1 %s -emit-llvm -o - -triple=armv7-unknown-linux | FileCheck %s // RUN: %clang_cc1 %s -emit-llvm -o - -triple=armv6-unknown-linux | FileCheck %s +// RUN: %clang_cc1 %s -emit-llvm -o - -triple=thumbv7-unknown-linux | FileCheck %s typedef int _Atomic_word; _Atomic_word exchange_and_add(volatile _Atomic_word *__mem, int __val) { -- cgit v1.2.3 From f039e3eb0ffa87aae0e38cec48f367ee179b4de6 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 14 May 2013 05:18:44 +0000 Subject: Suppress bogus "use of undefined constexpr function" error if the function body was erroneous and got discarded. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181758 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 5 +++++ test/SemaCXX/constant-expression-cxx11.cpp | 8 ++++++++ test/SemaCXX/enum-unscoped-nonexistent.cpp | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 0f44985471..2a3efb225b 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -3161,6 +3161,11 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc, Declaration->isConstexpr()) return false; + // Bail out with no diagnostic if the function declaration itself is invalid. + // We will have produced a relevant diagnostic while parsing it. + if (Declaration->isInvalidDecl()) + return false; + // Can we evaluate this function call? if (Definition && Definition->isConstexpr() && !Definition->isInvalidDecl()) return true; diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 09a9cb5dd8..97b0b91b99 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1503,3 +1503,11 @@ namespace PR15884 { // expected-note@-3 {{pointer to temporary is not a constant expression}} // expected-note@-4 {{temporary created here}} } + +namespace AfterError { + // FIXME: Suppress the 'no return statements' diagnostic if the body is invalid. + constexpr int error() { // expected-error {{no return statement}} + return foobar; // expected-error {{undeclared identifier}} + } + constexpr int k = error(); // expected-error {{must be initialized by a constant expression}} +} diff --git a/test/SemaCXX/enum-unscoped-nonexistent.cpp b/test/SemaCXX/enum-unscoped-nonexistent.cpp index e9da38f558..7da9a96d60 100644 --- a/test/SemaCXX/enum-unscoped-nonexistent.cpp +++ b/test/SemaCXX/enum-unscoped-nonexistent.cpp @@ -6,7 +6,7 @@ struct Base { template struct S : Base { enum E : int; constexpr int f() const; - constexpr int g() const; // expected-note {{declared here}} + constexpr int g() const; void h(); }; template<> enum S::E : int {}; // expected-note {{enum 'S::E' was explicitly specialized here}} @@ -23,7 +23,7 @@ static_assert(S().f() == 1, ""); // The unqualified-id here names a member of the current instantiation, which // bizarrely might not exist in some instantiations. template constexpr int S::g() const { return b; } // expected-error {{enumerator 'b' does not exist in instantiation of 'S'}} -static_assert(S().g() == 1, ""); // expected-note {{here}} expected-error {{not an integral constant expression}} expected-note {{undefined}} +static_assert(S().g() == 1, ""); // expected-note {{here}} expected-error {{not an integral constant expression}} static_assert(S().g() == 2, ""); static_assert(S().g() == 8, ""); -- cgit v1.2.3 From 3371711aabc154acb4bffee4dc9491632379857d Mon Sep 17 00:00:00 2001 From: Patrik Hagglund Date: Tue, 14 May 2013 07:53:53 +0000 Subject: Replace EXPECT_EQ with EXPECT_FALSE to avoid gcc warning [-Wconversion-null], introduced in r181326. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181761 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/Format/FormatTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 697cc53820..b2ae3d2560 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -4122,9 +4122,9 @@ TEST_F(FormatTest, ParsesConfiguration) { #define CHECK_PARSE_BOOL(FIELD) \ Style.FIELD = false; \ EXPECT_EQ(0, parseConfiguration(#FIELD ": true", &Style).value()); \ - EXPECT_EQ(true, Style.FIELD); \ + EXPECT_TRUE(Style.FIELD); \ EXPECT_EQ(0, parseConfiguration(#FIELD ": false", &Style).value()); \ - EXPECT_EQ(false, Style.FIELD); + EXPECT_FALSE(Style.FIELD); CHECK_PARSE_BOOL(AlignEscapedNewlinesLeft); CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine); -- cgit v1.2.3 From 028d59c9e5ef3c46ff26fe106050d750cf8bc2cc Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Tue, 14 May 2013 08:26:14 +0000 Subject: AArch64: correct definition of __clear_cache According to libgcc document __clear_cache takes two char* pointers. I suspect GCC's actual behaviour is more subtle than that, but char* should clearly be preferred to void*. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181762 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/BuiltinsAArch64.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clang/Basic/BuiltinsAArch64.def b/include/clang/Basic/BuiltinsAArch64.def index 9e9f6d0875..939c080cc5 100644 --- a/include/clang/Basic/BuiltinsAArch64.def +++ b/include/clang/Basic/BuiltinsAArch64.def @@ -15,4 +15,4 @@ // The format of this database matches clang/Basic/Builtins.def. // In libgcc -BUILTIN(__clear_cache, "vv*v*", "") +BUILTIN(__clear_cache, "vc*c*", "") -- cgit v1.2.3 From 7424d79613a67ba200b6801468f2b59cde7ab087 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Tue, 14 May 2013 08:34:47 +0000 Subject: Don't format sizeof/alignof as function types. Before: A a; After: A a; git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181764 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/TokenAnnotator.cpp | 4 +++- unittests/Format/FormatTest.cpp | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 29eaaa8b58..0f91b11033 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -173,7 +173,9 @@ private: } if (CurrentToken->isOneOf(tok::r_square, tok::r_brace)) return false; - if (CurrentToken->Parent->Type == TT_PointerOrReference && + if (Left->Parent && + !Left->Parent->isOneOf(tok::kw_sizeof, tok::kw_alignof) && + CurrentToken->Parent->Type == TT_PointerOrReference && CurrentToken->Parent->Parent->isOneOf(tok::l_paren, tok::coloncolon)) Left->DefinesFunctionType = true; updateParameterCount(Left, CurrentToken); diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index b2ae3d2560..b2393f578b 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -2790,6 +2790,10 @@ TEST_F(FormatTest, FormatsFunctionTypes) { verifyGoogleFormat("A;"); verifyGoogleFormat("void* (*a)(int);"); + + // Other constructs can look like function types: + verifyFormat("A a;"); + verifyFormat("A a;"); } TEST_F(FormatTest, BreaksLongDeclarations) { -- cgit v1.2.3 From a9d7b46c2fb3eb9020b60375e0cb722bd26b108d Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Tue, 14 May 2013 08:37:13 +0000 Subject: AArch64: add test for updated __clear_cache definition git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181765 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Sema/builtins-aarch64.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/Sema/builtins-aarch64.c b/test/Sema/builtins-aarch64.c index 03e03343eb..cc2a1549f7 100644 --- a/test/Sema/builtins-aarch64.c +++ b/test/Sema/builtins-aarch64.c @@ -1,4 +1,9 @@ // RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -DTEST1 -fsyntax-only -verify %s + +#ifdef TEST1 +void __clear_cache(char *start, char *end); +#endif void test_clear_cache_chars(char *start, char *end) { __clear_cache(start, end); -- cgit v1.2.3 From 748d38fcf762727e143ceb2352dc0dae56b34ffa Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Tue, 14 May 2013 08:48:24 +0000 Subject: Update clang-format emacs integration. - Remove free variables - Add function clang-format-buffer, e.g. for before-save-hooks - Wrap restoring windows in an unwind-protect Patch by Stephen Gildea! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181766 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/clang-format/clang-format.el | 44 +++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/tools/clang-format/clang-format.el b/tools/clang-format/clang-format.el index b76eb41d94..57475ac722 100644 --- a/tools/clang-format/clang-format.el +++ b/tools/clang-format/clang-format.el @@ -7,25 +7,39 @@ ;; (global-set-key [C-M-tab] 'clang-format-region) ;; ;; Depending on your configuration and coding style, you might need to modify -;; 'style' and 'binary' below. +;; 'style' in clang-format, below. + +;; *Location of the clang-format binary. If it is on your PATH, a full path name +;; need not be specified. +(defvar clang-format-binary "clang-format") + (defun clang-format-region () + "Use clang-format to format the currently active region." (interactive) + (let ((beg (if mark-active + (region-beginning) + (min (line-beginning-position) (1- (point-max))))) + (end (if mark-active + (region-end) + (line-end-position)))) + (clang-format beg end))) + +(defun clang-format-buffer () + "Use clang-format to format the current buffer." + (clang-format (point-min) (point-max))) +(defun clang-format (begin end) + "Use clang-format to format the code between BEGIN and END." (let* ((orig-windows (get-buffer-window-list (current-buffer))) (orig-window-starts (mapcar #'window-start orig-windows)) (orig-point (point)) - (binary "clang-format") (style "LLVM")) - (if mark-active - (setq beg (region-beginning) - end (region-end)) - (setq beg (min (line-beginning-position) (1- (point-max))) - end (line-end-position))) - (call-process-region (point-min) (point-max) binary t t nil - "-offset" (number-to-string (1- beg)) - "-length" (number-to-string (- end beg)) - "-style" style) - (goto-char orig-point) - (dotimes (index (length orig-windows)) - (set-window-start (nth index orig-windows) - (nth index orig-window-starts))))) + (unwind-protect + (call-process-region (point-min) (point-max) clang-format-binary t t nil + "-offset" (number-to-string (1- begin)) + "-length" (number-to-string (- end begin)) + "-style" style) + (goto-char orig-point) + (dotimes (index (length orig-windows)) + (set-window-start (nth index orig-windows) + (nth index orig-window-starts)))))) -- cgit v1.2.3 From 2a9805d227375efd988522873d4edc282010baae Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Tue, 14 May 2013 09:04:24 +0000 Subject: Implement string literal breaking on unbreakable token sequences. This fixes indentation where there are for example multiple closing parentheses after a string literal, and where those parentheses run over the end of the line. During testing this revealed a bug in the implementation of breakProtrudingToken: we don't want to change the state if we didn't actually do anything. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181767 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 41 ++++++++++++++++++++++++++++++---------- unittests/Format/FormatTest.cpp | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 50567a6f84..b8e0455c71 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -756,10 +756,18 @@ private: /// \brief If the current token sticks out over the end of the line, break /// it if possible. + /// + /// \returns An extra penalty if a token was broken, otherwise 0. + /// + /// Note that the penalty of the token protruding the allowed line length is + /// already handled in \c addNextStateToQueue; the returned penalty will only + /// cover the cost of the additional line breaks. unsigned breakProtrudingToken(const AnnotatedToken &Current, LineState &State, - bool DryRun) { + bool DryRun, + unsigned UnbreakableTailLength = 0) { llvm::OwningPtr Token; - unsigned StartColumn = State.Column - Current.FormatTok.TokenLength; + unsigned StartColumn = State.Column - Current.FormatTok.TokenLength - + UnbreakableTailLength; if (Current.is(tok::string_literal)) { // Only break up default narrow strings. const char *LiteralData = SourceMgr.getCharacterData( @@ -780,42 +788,55 @@ private: Current.Parent->Type != TT_ImplicitStringLiteral)) { Token.reset(new BreakableLineComment(SourceMgr, Current, StartColumn)); } else { - return 0; + // If a token that we cannot breaks protrudes, it means we were unable to + // break a sequence of tokens due to disallowed breaks between the tokens. + // Thus, we recursively search backwards to try to find a breakable token. + if (State.Column <= getColumnLimit() || + Current.CanBreakBefore || !Current.Parent) + return 0; + return breakProtrudingToken( + *Current.Parent, State, DryRun, + UnbreakableTailLength + Current.FormatTok.TokenLength); } + if (UnbreakableTailLength >= getColumnLimit()) + return 0; + unsigned RemainingSpace = getColumnLimit() - UnbreakableTailLength; bool BreakInserted = false; unsigned Penalty = 0; + unsigned PositionAfterLastLineInToken = 0; for (unsigned LineIndex = 0; LineIndex < Token->getLineCount(); ++LineIndex) { unsigned TailOffset = 0; - unsigned RemainingLength = + unsigned RemainingTokenLength = Token->getLineLengthAfterSplit(LineIndex, TailOffset); - while (RemainingLength > getColumnLimit()) { + while (RemainingTokenLength > RemainingSpace) { BreakableToken::Split Split = - Token->getSplit(LineIndex, TailOffset, getColumnLimit()); + Token->getSplit(LineIndex, TailOffset, RemainingSpace); if (Split.first == StringRef::npos) break; assert(Split.first != 0); - unsigned NewRemainingLength = Token->getLineLengthAfterSplit( + unsigned NewRemainingTokenLength = Token->getLineLengthAfterSplit( LineIndex, TailOffset + Split.first + Split.second); - if (NewRemainingLength >= RemainingLength) + if (NewRemainingTokenLength >= RemainingTokenLength) break; if (!DryRun) { Token->insertBreak(LineIndex, TailOffset, Split, Line.InPPDirective, Whitespaces); } TailOffset += Split.first + Split.second; - RemainingLength = NewRemainingLength; + RemainingTokenLength = NewRemainingTokenLength; Penalty += Style.PenaltyExcessCharacter; BreakInserted = true; } - State.Column = RemainingLength; + PositionAfterLastLineInToken = RemainingTokenLength; if (!DryRun) { Token->trimLine(LineIndex, TailOffset, Line.InPPDirective, Whitespaces); } } if (BreakInserted) { + State.Column = PositionAfterLastLineInToken + UnbreakableTailLength; for (unsigned i = 0, e = State.Stack.size(); i != e; ++i) State.Stack[i].BreakBeforeParameter = true; State.Stack.back().LastSpace = StartColumn; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index b2393f578b..b7f544e8a1 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -3944,6 +3944,48 @@ TEST_F(FormatTest, BreakStringLiterals) { format("#define A \"some text other\";", AlignLeft)); } +TEST_F(FormatTest, BreakStringLiteralsBeforeUnbreakableTokenSequence) { + EXPECT_EQ("someFunction(\"aaabbbcccd\"\n" + " \"ddeeefff\");", + format("someFunction(\"aaabbbcccdddeeefff\");", + getLLVMStyleWithColumns(25))); + EXPECT_EQ("someFunction1234567890(\n" + " \"aaabbbcccdddeeefff\");", + format("someFunction1234567890(\"aaabbbcccdddeeefff\");", + getLLVMStyleWithColumns(26))); + EXPECT_EQ("someFunction1234567890(\n" + " \"aaabbbcccdddeeeff\"\n" + " \"f\");", + format("someFunction1234567890(\"aaabbbcccdddeeefff\");", + getLLVMStyleWithColumns(25))); + EXPECT_EQ("someFunction1234567890(\n" + " \"aaabbbcccdddeeeff\"\n" + " \"f\");", + format("someFunction1234567890(\"aaabbbcccdddeeefff\");", + getLLVMStyleWithColumns(24))); + EXPECT_EQ("someFunction(\n" + " \"aaabbbcc \"\n" + " \"dddeeefff\");", + format("someFunction(\"aaabbbcc dddeeefff\");", + getLLVMStyleWithColumns(25))); + EXPECT_EQ("someFunction(\"aaabbbccc \"\n" + " \"ddeeefff\");", + format("someFunction(\"aaabbbccc ddeeefff\");", + getLLVMStyleWithColumns(25))); + EXPECT_EQ("someFunction1234567890(\n" + " \"aaabb \"\n" + " \"cccdddeeefff\");", + format("someFunction1234567890(\"aaabb cccdddeeefff\");", + getLLVMStyleWithColumns(25))); + EXPECT_EQ("#define A \\\n" + " string s = \\\n" + " \"123456789\" \\\n" + " \"0\"; \\\n" + " int i;", + format("#define A string s = \"1234567890\"; int i;", + getLLVMStyleWithColumns(20))); +} + TEST_F(FormatTest, DoNotBreakStringLiteralsInEscapeSequence) { EXPECT_EQ("\"\\a\"", format("\"\\a\"", getLLVMStyleWithColumns(3))); -- cgit v1.2.3 From f7f295f321fd434e1e542844a71f538a56f2f8fb Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Tue, 14 May 2013 09:13:00 +0000 Subject: First revision of the dynamic ASTMatcher library. This library supports all the features of the compile-time based ASTMatcher library, but allows the user to specify and construct the matchers at runtime. It contains the following modules: - A variant type, to be used by the matcher factory. - A registry, where the matchers are indexed by name and have a factory method with a generic signature. - A simple matcher expression parser, that can be used to convert a matcher expression string into actual matchers that can be used with the AST at runtime. Many features where omitted from this first revision to simplify this code review. The main ideas are still represented in this change and it already has support working use cases. Things that are missing: - Support for polymorphic matchers. These requires supporting code in the registry, the marshallers and the variant type. - Support for numbers, char and bool arguments to the matchers. This requires supporting code in the parser and the variant type. - A command line program putting everything together and providing an already functional tool. Patch by Samuel Benzaquen. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181768 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/ASTMatchers/ASTMatchFinder.h | 11 + include/clang/ASTMatchers/ASTMatchersInternal.h | 6 + include/clang/ASTMatchers/Dynamic/Diagnostics.h | 109 +++++++ include/clang/ASTMatchers/Dynamic/Parser.h | 143 +++++++++ include/clang/ASTMatchers/Dynamic/Registry.h | 63 ++++ include/clang/ASTMatchers/Dynamic/VariantValue.h | 125 ++++++++ lib/ASTMatchers/ASTMatchFinder.cpp | 8 + lib/ASTMatchers/CMakeLists.txt | 2 + lib/ASTMatchers/Dynamic/CMakeLists.txt | 12 + lib/ASTMatchers/Dynamic/Diagnostics.cpp | 113 +++++++ lib/ASTMatchers/Dynamic/Makefile | 13 + lib/ASTMatchers/Dynamic/Marshallers.h | 223 ++++++++++++++ lib/ASTMatchers/Dynamic/Parser.cpp | 332 +++++++++++++++++++++ lib/ASTMatchers/Dynamic/Registry.cpp | 153 ++++++++++ lib/ASTMatchers/Dynamic/VariantValue.cpp | 105 +++++++ lib/ASTMatchers/Makefile | 2 + unittests/ASTMatchers/ASTMatchersTest.h | 35 +++ unittests/ASTMatchers/CMakeLists.txt | 2 + unittests/ASTMatchers/Dynamic/CMakeLists.txt | 7 + unittests/ASTMatchers/Dynamic/Makefile | 18 ++ unittests/ASTMatchers/Dynamic/ParserTest.cpp | 194 ++++++++++++ unittests/ASTMatchers/Dynamic/RegistryTest.cpp | 112 +++++++ unittests/ASTMatchers/Dynamic/VariantValueTest.cpp | 97 ++++++ unittests/ASTMatchers/Makefile | 2 + 24 files changed, 1887 insertions(+) create mode 100644 include/clang/ASTMatchers/Dynamic/Diagnostics.h create mode 100644 include/clang/ASTMatchers/Dynamic/Parser.h create mode 100644 include/clang/ASTMatchers/Dynamic/Registry.h create mode 100644 include/clang/ASTMatchers/Dynamic/VariantValue.h create mode 100644 lib/ASTMatchers/Dynamic/CMakeLists.txt create mode 100644 lib/ASTMatchers/Dynamic/Diagnostics.cpp create mode 100644 lib/ASTMatchers/Dynamic/Makefile create mode 100644 lib/ASTMatchers/Dynamic/Marshallers.h create mode 100644 lib/ASTMatchers/Dynamic/Parser.cpp create mode 100644 lib/ASTMatchers/Dynamic/Registry.cpp create mode 100644 lib/ASTMatchers/Dynamic/VariantValue.cpp create mode 100644 unittests/ASTMatchers/Dynamic/CMakeLists.txt create mode 100644 unittests/ASTMatchers/Dynamic/Makefile create mode 100644 unittests/ASTMatchers/Dynamic/ParserTest.cpp create mode 100644 unittests/ASTMatchers/Dynamic/RegistryTest.cpp create mode 100644 unittests/ASTMatchers/Dynamic/VariantValueTest.cpp diff --git a/include/clang/ASTMatchers/ASTMatchFinder.h b/include/clang/ASTMatchers/ASTMatchFinder.h index 870a39b391..be58afffd1 100644 --- a/include/clang/ASTMatchers/ASTMatchFinder.h +++ b/include/clang/ASTMatchers/ASTMatchFinder.h @@ -131,6 +131,17 @@ public: MatchCallback *Action); /// @} + /// \brief Adds a matcher to execute when running over the AST. + /// + /// This is similar to \c addMatcher(), but it uses the dynamic interface. It + /// is more flexible, but the lost type information enables a caller to pass + /// a matcher that cannot match anything. + /// + /// \returns \c true if the matcher is a valid top-level matcher, \c false + /// otherwise. + bool addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, + MatchCallback *Action); + /// \brief Creates a clang ASTConsumer that finds all matches. clang::ASTConsumer *newASTConsumer(); diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h index 30691ad8f9..bc4ddce36f 100644 --- a/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -239,6 +239,9 @@ public: ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const = 0; + /// \brief Makes a copy of this matcher object. + virtual DynTypedMatcher *clone() const = 0; + /// \brief Returns a unique ID for the matcher. virtual uint64_t getID() const = 0; }; @@ -301,6 +304,9 @@ public: return matches(*Node, Finder, Builder); } + /// \brief Makes a copy of this matcher object. + virtual Matcher *clone() const { return new Matcher(*this); } + /// \brief Allows the conversion of a \c Matcher to a \c /// Matcher. /// diff --git a/include/clang/ASTMatchers/Dynamic/Diagnostics.h b/include/clang/ASTMatchers/Dynamic/Diagnostics.h new file mode 100644 index 0000000000..417bc67967 --- /dev/null +++ b/include/clang/ASTMatchers/Dynamic/Diagnostics.h @@ -0,0 +1,109 @@ +//===--- Diagnostics.h - Helper class for error diagnostics -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Diagnostics class to manage error messages. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_DIAGNOSTICS_H +#define LLVM_CLANG_AST_MATCHERS_DYNAMIC_DIAGNOSTICS_H + +#include +#include + +#include "clang/ASTMatchers/Dynamic/VariantValue.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { + +struct SourceLocation { + SourceLocation() : Line(), Column() {} + unsigned Line; + unsigned Column; +}; + +struct SourceRange { + SourceLocation Start; + SourceLocation End; +}; + +/// \brief A VariantValue instance annotated with its parser context. +struct ParserValue { + ParserValue() : Text(), Range(), Value() {} + StringRef Text; + SourceRange Range; + VariantValue Value; +}; + +/// \brief Helper class to manage error messages. +class Diagnostics { + public: + /// \brief All errors from the system. + enum ErrorType { + ET_None = 0, + + ET_RegistryNotFound = 1, + ET_RegistryWrongArgCount = 2, + ET_RegistryWrongArgType = 3, + + ET_ParserStringError = 100, + ET_ParserMatcherArgFailure = 101, + ET_ParserMatcherFailure = 102, + ET_ParserNoOpenParen = 103, + ET_ParserNoCloseParen = 104, + ET_ParserNoComma = 105, + ET_ParserNoCode = 106, + ET_ParserNotAMatcher = 107, + ET_ParserInvalidToken = 108 + }; + + /// \brief Helper stream class. + struct ArgStream { + template ArgStream &operator<<(const T &Arg) { + return operator<<(Twine(Arg)); + } + ArgStream &operator<<(const Twine &Arg); + std::vector *Out; + }; + + /// \brief Push a frame to the beginning of the list + /// + /// Returns a helper class to allow the caller to pass the arguments for the + /// error message, using the << operator. + ArgStream pushErrorFrame(const SourceRange &Range, ErrorType Error); + + struct ErrorFrame { + SourceRange Range; + ErrorType Type; + std::vector Args; + + std::string ToString() const; + }; + ArrayRef frames() const { return Frames; } + + /// \brief Returns a string representation of the last frame. + std::string ToString() const; + /// \brief Returns a string representation of the whole frame stack. + std::string ToStringFull() const; + + private: + std::vector Frames; +}; + +} // namespace dynamic +} // namespace ast_matchers +} // namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_DIAGNOSTICS_H diff --git a/include/clang/ASTMatchers/Dynamic/Parser.h b/include/clang/ASTMatchers/Dynamic/Parser.h new file mode 100644 index 0000000000..f981c6055e --- /dev/null +++ b/include/clang/ASTMatchers/Dynamic/Parser.h @@ -0,0 +1,143 @@ +//===--- Parser.h - Matcher expression parser -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Simple matcher expression parser. +/// +/// The parser understands matcher expressions of the form: +/// MatcherName(Arg0, Arg1, ..., ArgN) +/// as well as simple types like strings. +/// The parser does not know how to process the matchers. It delegates this task +/// to a Sema object received as an argument. +/// +/// \code +/// Grammar for the expressions supported: +/// := | +/// := "quoted string" +/// := () +/// := [a-zA-Z]+ +/// := | , +/// \endcode +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_PARSER_H +#define LLVM_CLANG_AST_MATCHERS_DYNAMIC_PARSER_H + +#include "clang/ASTMatchers/Dynamic/Diagnostics.h" +#include "clang/ASTMatchers/Dynamic/VariantValue.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { + +/// \brief Matcher expression parser. +class Parser { +public: + /// \brief Interface to connect the parser with the registry and more. + /// + /// The parser uses the Sema instance passed into + /// parseMatcherExpression() to handle all matcher tokens. The simplest + /// processor implementation would simply call into the registry to create + /// the matchers. + /// However, a more complex processor might decide to intercept the matcher + /// creation and do some extra work. For example, it could apply some + /// transformation to the matcher by adding some id() nodes, or could detect + /// specific matcher nodes for more efficient lookup. + class Sema { + public: + virtual ~Sema(); + + /// \brief Process a matcher expression. + /// + /// All the arguments passed here have already been processed. + /// + /// \param MatcherName The matcher name found by the parser. + /// + /// \param NameRange The location of the name in the matcher source. + /// Useful for error reporting. + /// + /// \param Args The argument list for the matcher. + /// + /// \return The matcher object constructed by the processor, or NULL + /// if an error occurred. In that case, \c Error will contain a + /// description of the error. + /// The caller takes ownership of the DynTypedMatcher object returned. + virtual DynTypedMatcher * + actOnMatcherExpression(StringRef MatcherName, const SourceRange &NameRange, + ArrayRef Args, Diagnostics *Error) = 0; + }; + + /// \brief Parse a matcher expression, creating matchers from the registry. + /// + /// This overload creates matchers calling directly into the registry. If the + /// caller needs more control over how the matchers are created, then it can + /// use the overload below that takes a Sema. + /// + /// \param MatcherCode The matcher expression to parse. + /// + /// \return The matcher object constructed, or NULL if an error occurred. + // In that case, \c Error will contain a description of the error. + /// The caller takes ownership of the DynTypedMatcher object returned. + static DynTypedMatcher *parseMatcherExpression(StringRef MatcherCode, + Diagnostics *Error); + + /// \brief Parse a matcher expression. + /// + /// \param MatcherCode The matcher expression to parse. + /// + /// \param S The Sema instance that will help the parser + /// construct the matchers. + /// \return The matcher object constructed by the processor, or NULL + /// if an error occurred. In that case, \c Error will contain a + /// description of the error. + /// The caller takes ownership of the DynTypedMatcher object returned. + static DynTypedMatcher *parseMatcherExpression(StringRef MatcherCode, + Sema *S, + Diagnostics *Error); + + /// \brief Parse an expression, creating matchers from the registry. + /// + /// Parses any expression supported by this parser. In general, the + /// \c parseMatcherExpression function is a better approach to get a matcher + /// object. + static bool parseExpression(StringRef Code, VariantValue *Value, + Diagnostics *Error); + + /// \brief Parse an expression. + /// + /// Parses any expression supported by this parser. In general, the + /// \c parseMatcherExpression function is a better approach to get a matcher + /// object. + static bool parseExpression(StringRef Code, Sema *S, + VariantValue *Value, Diagnostics *Error); + +private: + class CodeTokenizer; + struct TokenInfo; + + Parser(CodeTokenizer *Tokenizer, Sema *S, + Diagnostics *Error); + + bool parseExpressionImpl(VariantValue *Value); + bool parseMatcherExpressionImpl(VariantValue *Value); + + CodeTokenizer *const Tokenizer; + Sema *const S; + Diagnostics *const Error; +}; + +} // namespace dynamic +} // namespace ast_matchers +} // namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_PARSER_H diff --git a/include/clang/ASTMatchers/Dynamic/Registry.h b/include/clang/ASTMatchers/Dynamic/Registry.h new file mode 100644 index 0000000000..b092ed8fe1 --- /dev/null +++ b/include/clang/ASTMatchers/Dynamic/Registry.h @@ -0,0 +1,63 @@ +//===--- Registry.h - Matcher registry -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Registry of all known matchers. +/// +/// The registry provides a generic interface to construct any matcher by name. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_REGISTRY_H +#define LLVM_CLANG_AST_MATCHERS_DYNAMIC_REGISTRY_H + +#include "clang/ASTMatchers/Dynamic/Diagnostics.h" +#include "clang/ASTMatchers/Dynamic/VariantValue.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { + +class Registry { +public: + /// \brief Construct a matcher from the registry by name. + /// + /// Consult the registry of known matchers and construct the appropriate + /// matcher by name. + /// + /// \param MatcherName The name of the matcher to instantiate. + /// + /// \param NameRange The location of the name in the matcher source. + /// Useful for error reporting. + /// + /// \param Args The argument list for the matcher. The number and types of the + /// values must be valid for the matcher requested. Otherwise, the function + /// will return an error. + /// + /// \return The matcher if no error was found. NULL if the matcher is not + // found, or if the number of arguments or argument types do not + /// match the signature. In that case \c Error will contain the description + /// of the error. + static DynTypedMatcher *constructMatcher(StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef Args, + Diagnostics *Error); + +private: + Registry() LLVM_DELETED_FUNCTION; +}; + +} // namespace dynamic +} // namespace ast_matchers +} // namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_REGISTRY_H diff --git a/include/clang/ASTMatchers/Dynamic/VariantValue.h b/include/clang/ASTMatchers/Dynamic/VariantValue.h new file mode 100644 index 0000000000..8dc4238757 --- /dev/null +++ b/include/clang/ASTMatchers/Dynamic/VariantValue.h @@ -0,0 +1,125 @@ +//===--- VariantValue.h - Polymorphic value type -*- C++ -*-===/ +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Polymorphic value type. +/// +/// Supports all the types required for dynamic Matcher construction. +/// Used by the registry to construct matchers in a generic way. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_VARIANT_VALUE_H +#define LLVM_CLANG_AST_MATCHERS_DYNAMIC_VARIANT_VALUE_H + +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/type_traits.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { + +typedef ast_matchers::internal::DynTypedMatcher DynTypedMatcher; + +/// \brief Variant value class. +/// +/// Basically, a tagged union with value type semantics. +/// It is used by the registry as the return value and argument type for the +/// matcher factory methods. +/// It can be constructed from any of the supported types. It supports +/// copy/assignment. +/// +/// Supported types: +/// - \c std::string +/// - \c DynTypedMatcher, and any \c Matcher +class VariantValue { +public: + VariantValue() : Type(VT_Nothing) {} + + VariantValue(const VariantValue &Other); + ~VariantValue(); + VariantValue &operator=(const VariantValue &Other); + + /// \brief Specific constructors for each supported type. + VariantValue(const std::string &String); + VariantValue(const DynTypedMatcher &Matcher); + + /// \brief String value functions. + bool isString() const; + const std::string &getString() const; + void setString(const std::string &String); + + /// \brief Matcher value functions. + bool isMatcher() const; + const DynTypedMatcher &getMatcher() const; + void setMatcher(const DynTypedMatcher &Matcher); + /// \brief Set the value to be \c Matcher by taking ownership of the object. + void takeMatcher(DynTypedMatcher *Matcher); + + /// \brief Specialized Matcher is/get functions. + template + bool isTypedMatcher() const { + // TODO: Add some logic to test if T is actually valid for the underlying + // type of the matcher. + return isMatcher(); + } + + template + ast_matchers::internal::Matcher getTypedMatcher() const { + return ast_matchers::internal::makeMatcher( + new DerivedTypeMatcher(getMatcher())); + } + +private: + void reset(); + + /// \brief Matcher bridge between a Matcher and a generic DynTypedMatcher. + template + class DerivedTypeMatcher : + public ast_matchers::internal::MatcherInterface { + public: + explicit DerivedTypeMatcher(const DynTypedMatcher &DynMatcher) + : DynMatcher(DynMatcher.clone()) {} + virtual ~DerivedTypeMatcher() {} + + typedef ast_matchers::internal::ASTMatchFinder ASTMatchFinder; + typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder; + bool matches(const T &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return DynMatcher->matches(ast_type_traits::DynTypedNode::create(Node), + Finder, Builder); + } + + private: + const OwningPtr DynMatcher; + }; + + /// \brief All supported value types. + enum ValueType { + VT_Nothing, + VT_String, + VT_Matcher + }; + + /// \brief All supported value types. + union AllValues { + std::string *String; + DynTypedMatcher *Matcher; + }; + + ValueType Type; + AllValues Value; +}; + +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_VARIANT_VALUE_H diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp index 6ebd736e3c..eccc9cd4d3 100644 --- a/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -744,6 +744,14 @@ void MatchFinder::addMatcher(const TypeLocMatcher &NodeMatch, new TypeLocMatcher(NodeMatch), Action)); } +bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, + MatchCallback *Action) { + MatcherCallbackPairs.push_back(std::make_pair(NodeMatch.clone(), Action)); + // TODO: Do runtime type checking to make sure the matcher is one of the valid + // top-level matchers. + return true; +} + ASTConsumer *MatchFinder::newASTConsumer() { return new internal::MatchASTConsumer(&MatcherCallbackPairs, ParsingDone); } diff --git a/lib/ASTMatchers/CMakeLists.txt b/lib/ASTMatchers/CMakeLists.txt index 86560d61c9..4a390a8fc3 100644 --- a/lib/ASTMatchers/CMakeLists.txt +++ b/lib/ASTMatchers/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(Dynamic) + set(LLVM_LINK_COMPONENTS support) add_clang_library(clangASTMatchers diff --git a/lib/ASTMatchers/Dynamic/CMakeLists.txt b/lib/ASTMatchers/Dynamic/CMakeLists.txt new file mode 100644 index 0000000000..ba920a4f67 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/CMakeLists.txt @@ -0,0 +1,12 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangDynamicASTMatchers + Diagnostics.cpp + VariantValue.cpp + Parser.cpp + Registry.cpp + ) + +add_dependencies(clangDynamicASTMatchers + clangASTMatchers + ) diff --git a/lib/ASTMatchers/Dynamic/Diagnostics.cpp b/lib/ASTMatchers/Dynamic/Diagnostics.cpp new file mode 100644 index 0000000000..fb3cac370f --- /dev/null +++ b/lib/ASTMatchers/Dynamic/Diagnostics.cpp @@ -0,0 +1,113 @@ +//===--- Diagnostics.cpp - Helper class for error diagnostics -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/Dynamic/Diagnostics.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { + +Diagnostics::ArgStream & +Diagnostics::ArgStream::operator<<(const Twine &Arg) { + Out->push_back(Arg.str()); + return *this; +} + +Diagnostics::ArgStream Diagnostics::pushErrorFrame(const SourceRange &Range, + ErrorType Error) { + Frames.insert(Frames.begin(), ErrorFrame()); + ErrorFrame &Last = Frames.front(); + Last.Range = Range; + Last.Type = Error; + ArgStream Out = { &Last.Args }; + return Out; +} + +StringRef ErrorTypeToString(Diagnostics::ErrorType Type) { + switch (Type) { + case Diagnostics::ET_RegistryNotFound: + return "Matcher not found: $0"; + case Diagnostics::ET_RegistryWrongArgCount: + return "Incorrect argument count. (Expected = $0) != (Actual = $1)"; + case Diagnostics::ET_RegistryWrongArgType: + return "Incorrect type on function $0 for arg $1."; + + case Diagnostics::ET_ParserStringError: + return "Error parsing string token: <$0>"; + case Diagnostics::ET_ParserMatcherArgFailure: + return "Error parsing argument $0 for matcher $1."; + case Diagnostics::ET_ParserMatcherFailure: + return "Error building matcher $0."; + case Diagnostics::ET_ParserNoOpenParen: + return "Error parsing matcher. Found token <$0> while looking for '('."; + case Diagnostics::ET_ParserNoCloseParen: + return "Error parsing matcher. Found end-of-code while looking for ')'."; + case Diagnostics::ET_ParserNoComma: + return "Error parsing matcher. Found token <$0> while looking for ','."; + case Diagnostics::ET_ParserNoCode: + return "End of code found while looking for token."; + case Diagnostics::ET_ParserNotAMatcher: + return "Input value is not a matcher expression."; + case Diagnostics::ET_ParserInvalidToken: + return "Invalid token <$0> found when looking for a value."; + + case Diagnostics::ET_None: + return ""; + } + llvm_unreachable("Unknown ErrorType value."); +} + +std::string FormatErrorString(StringRef FormatString, + ArrayRef Args) { + std::string Out; + while (!FormatString.empty()) { + std::pair Pieces = FormatString.split("$"); + Out += Pieces.first.str(); + if (Pieces.second.empty()) break; + + const char Next = Pieces.second.front(); + FormatString = Pieces.second.drop_front(); + if (Next >= '0' && Next <= '9') { + const unsigned Index = Next - '0'; + if (Index < Args.size()) { + Out += Args[Index]; + } else { + Out += ""; + } + } + } + return Out; +} + +std::string Diagnostics::ErrorFrame::ToString() const { + StringRef FormatString = ErrorTypeToString(Type); + std::string ErrorOut = FormatErrorString(FormatString, Args); + if (Range.Start.Line > 0 && Range.Start.Column > 0) + return (Twine(Range.Start.Line) + ":" + Twine(Range.Start.Column) + ": " + + ErrorOut).str(); + return ErrorOut; +} + +std::string Diagnostics::ToString() const { + if (Frames.empty()) return ""; + return Frames[Frames.size() - 1].ToString(); +} + +std::string Diagnostics::ToStringFull() const { + std::string Result; + for (size_t i = 0, end = Frames.size(); i != end; ++i) { + if (i > 0) Result += "\n"; + Result += Frames[i].ToString(); + } + return Result; +} + +} // namespace dynamic +} // namespace ast_matchers +} // namespace clang diff --git a/lib/ASTMatchers/Dynamic/Makefile b/lib/ASTMatchers/Dynamic/Makefile new file mode 100644 index 0000000000..a57d752229 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/Makefile @@ -0,0 +1,13 @@ +##===- clang/lib/ASTMatchers/Dynamic/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../../.. +LIBRARYNAME := clangDynamicASTMatchers + +include $(CLANG_LEVEL)/Makefile diff --git a/lib/ASTMatchers/Dynamic/Marshallers.h b/lib/ASTMatchers/Dynamic/Marshallers.h new file mode 100644 index 0000000000..e75a175738 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/Marshallers.h @@ -0,0 +1,223 @@ +//===--- Marshallers.h - Generic matcher function marshallers -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Functions templates and classes to wrap matcher construct functions. +/// +/// A collection of template function and classes that provide a generic +/// marshalling layer on top of matcher construct functions. +/// These are used by the registry to export all marshaller constructors with +/// the same generic interface. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_MATCHERS_DYNAMIC_MARSHALLERS_H +#define LLVM_CLANG_AST_MATCHERS_DYNAMIC_MARSHALLERS_H + +#include +#include +#include + +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/Dynamic/Diagnostics.h" +#include "clang/ASTMatchers/Dynamic/VariantValue.h" +#include "clang/Basic/LLVM.h" +#include "llvm/Support/type_traits.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { + +namespace internal { + +/// \brief Helper template class to just from argument type to the right is/get +/// functions in VariantValue. +/// Used to verify and extract the matcher arguments below. +template struct ArgTypeTraits; +template struct ArgTypeTraits : public ArgTypeTraits { +}; + +template <> struct ArgTypeTraits { + static bool is(const VariantValue &Value) { return Value.isString(); } + static const std::string &get(const VariantValue &Value) { + return Value.getString(); + } +}; + +template struct ArgTypeTraits > { + static bool is(const VariantValue &Value) { return Value.isMatcher(); } + static ast_matchers::internal::Matcher get(const VariantValue &Value) { + return Value.getTypedMatcher(); + } + +}; + +/// \brief Generic MatcherCreate interface. +/// +/// Provides a \c run() method that constructs the matcher from the provided +/// arguments. +class MatcherCreateCallback { +public: + virtual ~MatcherCreateCallback() {} + virtual DynTypedMatcher *run(const SourceRange &NameRange, + ArrayRef Args, + Diagnostics *Error) const = 0; +}; + +/// \brief Simple callback implementation. Marshaller and function are provided. +/// +/// \param Marshaller Function to unpack the arguments and call \c Func +/// \param Func Matcher construct function. This is the function that +/// compile-time matcher expressions would use to create the matcher. +template +class FixedArgCountMatcherCreateCallback : public MatcherCreateCallback { +public: + FixedArgCountMatcherCreateCallback(MarshallerType Marshaller, FuncType Func, + StringRef MatcherName) + : Marshaller(Marshaller), Func(Func), MatcherName(MatcherName.str()) {} + + DynTypedMatcher *run(const SourceRange &NameRange, + ArrayRef Args, Diagnostics *Error) const { + return Marshaller(Func, MatcherName, NameRange, Args, Error); + } + +private: + const MarshallerType Marshaller; + const FuncType Func; + const std::string MatcherName; +}; + +/// \brief Helper function to do template argument deduction. +template +MatcherCreateCallback * +createMarshallerCallback(MarshallerType Marshaller, FuncType Func, + StringRef MatcherName) { + return new FixedArgCountMatcherCreateCallback( + Marshaller, Func, MatcherName); +} + +/// \brief Helper macros to check the arguments on all marshaller functions. +#define CHECK_ARG_COUNT(count) \ + if (Args.size() != count) { \ + Error->pushErrorFrame(NameRange, Error->ET_RegistryWrongArgCount) \ + << count << Args.size(); \ + return NULL; \ + } + +#define CHECK_ARG_TYPE(index, type) \ + if (!ArgTypeTraits::is(Args[index].Value)) { \ + Error->pushErrorFrame(Args[index].Range, Error->ET_RegistryWrongArgType) \ + << MatcherName << (index + 1); \ + return NULL; \ + } + +/// \brief Metafunction to normalize argument types. +/// +/// We need to remove the const& out of the function parameters to be able to +/// find values on VariantValue. +template +struct remove_const_ref : + public llvm::remove_const::type> { +}; + +/// \brief 0-arg marshaller function. +template +DynTypedMatcher *matcherMarshall0(ReturnType (*Func)(), StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef Args, + Diagnostics *Error) { + CHECK_ARG_COUNT(0); + return Func().clone(); +} + +/// \brief 1-arg marshaller function. +template +DynTypedMatcher *matcherMarshall1(ReturnType (*Func)(InArgType1), + StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef Args, + Diagnostics *Error) { + typedef typename remove_const_ref::type ArgType1; + CHECK_ARG_COUNT(1); + CHECK_ARG_TYPE(0, ArgType1); + return Func(ArgTypeTraits::get(Args[0].Value)).clone(); +} + +/// \brief Variadic marshaller function. +template +class VariadicMatcherCreateCallback : public MatcherCreateCallback { +public: + explicit VariadicMatcherCreateCallback(StringRef MatcherName) + : MatcherName(MatcherName.str()) {} + + typedef ast_matchers::internal::Matcher DerivedMatcherType; + + DynTypedMatcher *run(const SourceRange &NameRange, ArrayRef Args, + Diagnostics *Error) const { + std::list References; + std::vector InnerArgs(Args.size()); + for (size_t i = 0, e = Args.size(); i != e; ++i) { + CHECK_ARG_TYPE(i, DerivedMatcherType); + References.push_back( + ArgTypeTraits::get(Args[i].Value)); + InnerArgs[i] = &References.back(); + } + return ast_matchers::internal::makeDynCastAllOfComposite( + ArrayRef(InnerArgs)).clone(); + } + +private: + const std::string MatcherName; +}; + +#undef CHECK_ARG_COUNT +#undef CHECK_ARG_TYPE + +/// Helper functions to select the appropriate marshaller functions. +/// They detects the number of arguments, arguments types and return type. + +/// \brief 0-arg overload +template +MatcherCreateCallback *makeMatcherAutoMarshall(ReturnType (*Func)(), + StringRef MatcherName) { + return createMarshallerCallback(matcherMarshall0, Func, + MatcherName); +} + +/// \brief 1-arg overload +template +MatcherCreateCallback *makeMatcherAutoMarshall(ReturnType (*Func)(ArgType1), + StringRef MatcherName) { + return createMarshallerCallback(matcherMarshall1, Func, + MatcherName); +} + +/// \brief Variadic overload. +template +MatcherCreateCallback *makeMatcherAutoMarshall( + ast_matchers::internal::VariadicAllOfMatcher Func, + StringRef MatcherName) { + return new VariadicMatcherCreateCallback( + MatcherName); +} + +template +MatcherCreateCallback * +makeMatcherAutoMarshall(ast_matchers::internal::VariadicDynCastAllOfMatcher< + BaseType, MatcherType> Func, + StringRef MatcherName) { + return new VariadicMatcherCreateCallback(MatcherName); +} + +} // namespace internal +} // namespace dynamic +} // namespace ast_matchers +} // namespace clang + +#endif // LLVM_CLANG_AST_MATCHERS_DYNAMIC_MARSHALLERS_H diff --git a/lib/ASTMatchers/Dynamic/Parser.cpp b/lib/ASTMatchers/Dynamic/Parser.cpp new file mode 100644 index 0000000000..1678820da0 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/Parser.cpp @@ -0,0 +1,332 @@ +//===--- Parser.cpp - Matcher expression parser -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Recursive parser implementation for the matcher expression grammar. +/// +//===----------------------------------------------------------------------===// + +#include +#include + +#include "clang/ASTMatchers/Dynamic/Parser.h" +#include "clang/ASTMatchers/Dynamic/Registry.h" +#include "clang/Basic/CharInfo.h" +#include "llvm/ADT/Twine.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { + +/// \brief Simple structure to hold information for one token from the parser. +struct Parser::TokenInfo { + /// \brief Different possible tokens. + enum TokenKind { + TK_Eof = 0, + TK_OpenParen = 1, + TK_CloseParen = 2, + TK_Comma = 3, + TK_Literal = 4, + TK_Ident = 5, + TK_InvalidChar = 6, + TK_Error = 7 + }; + + TokenInfo() : Text(), Kind(TK_Eof), Range(), Value() {} + + StringRef Text; + TokenKind Kind; + SourceRange Range; + VariantValue Value; +}; + +/// \brief Simple tokenizer for the parser. +class Parser::CodeTokenizer { +public: + explicit CodeTokenizer(StringRef MatcherCode, Diagnostics *Error) + : Code(MatcherCode), StartOfLine(MatcherCode), Line(1), Error(Error) { + NextToken = getNextToken(); + } + + /// \brief Returns but doesn't consume the next token. + const TokenInfo &peekNextToken() const { return NextToken; } + + /// \brief Consumes and returns the next token. + TokenInfo consumeNextToken() { + TokenInfo ThisToken = NextToken; + NextToken = getNextToken(); + return ThisToken; + } + + TokenInfo::TokenKind nextTokenKind() const { return NextToken.Kind; } + +private: + TokenInfo getNextToken() { + consumeWhitespace(); + TokenInfo Result; + Result.Range.Start = currentLocation(); + + if (Code.empty()) { + Result.Kind = TokenInfo::TK_Eof; + Result.Text = ""; + return Result; + } + + switch (Code[0]) { + case ',': + Result.Kind = TokenInfo::TK_Comma; + Result.Text = Code.substr(0, 1); + Code = Code.drop_front(); + break; + case '(': + Result.Kind = TokenInfo::TK_OpenParen; + Result.Text = Code.substr(0, 1); + Code = Code.drop_front(); + break; + case ')': + Result.Kind = TokenInfo::TK_CloseParen; + Result.Text = Code.substr(0, 1); + Code = Code.drop_front(); + break; + + case '"': + case '\'': + // Parse a string literal. + consumeStringLiteral(&Result); + break; + + default: + if (isAlphanumeric(Code[0])) { + // Parse an identifier + size_t TokenLength = 1; + while (TokenLength < Code.size() && isAlphanumeric(Code[TokenLength])) + ++TokenLength; + Result.Kind = TokenInfo::TK_Ident; + Result.Text = Code.substr(0, TokenLength); + Code = Code.drop_front(TokenLength); + } else { + Result.Kind = TokenInfo::TK_InvalidChar; + Result.Text = Code.substr(0, 1); + Code = Code.drop_front(1); + } + break; + } + + Result.Range.End = currentLocation(); + return Result; + } + + /// \brief Consume a string literal. + /// + /// \c Code must be positioned at the start of the literal (the opening + /// quote). Consumed until it finds the same closing quote character. + void consumeStringLiteral(TokenInfo *Result) { + bool InEscape = false; + const char Marker = Code[0]; + for (size_t Length = 1, Size = Code.size(); Length != Size; ++Length) { + if (InEscape) { + InEscape = false; + continue; + } + if (Code[Length] == '\\') { + InEscape = true; + continue; + } + if (Code[Length] == Marker) { + Result->Kind = TokenInfo::TK_Literal; + Result->Text = Code.substr(0, Length + 1); + Result->Value = Code.substr(1, Length - 1).str(); + Code = Code.drop_front(Length + 1); + return; + } + } + + StringRef ErrorText = Code; + Code = Code.drop_front(Code.size()); + SourceRange Range; + Range.Start = Result->Range.Start; + Range.End = currentLocation(); + Error->pushErrorFrame(Range, Error->ET_ParserStringError) + << ErrorText; + Result->Kind = TokenInfo::TK_Error; + } + + /// \brief Consume all leading whitespace from \c Code. + void consumeWhitespace() { + while (!Code.empty() && isWhitespace(Code[0])) { + if (Code[0] == '\n') { + ++Line; + StartOfLine = Code.drop_front(); + } + Code = Code.drop_front(); + } + } + + SourceLocation currentLocation() { + SourceLocation Location; + Location.Line = Line; + Location.Column = Code.data() - StartOfLine.data() + 1; + return Location; + } + + StringRef Code; + StringRef StartOfLine; + unsigned Line; + Diagnostics *Error; + TokenInfo NextToken; +}; + +Parser::Sema::~Sema() {} + +/// \brief Parse and validate a matcher expression. +/// \return \c true on success, in which case \c Value has the matcher parsed. +/// If the input is malformed, or some argument has an error, it +/// returns \c false. +bool Parser::parseMatcherExpressionImpl(VariantValue *Value) { + const TokenInfo NameToken = Tokenizer->consumeNextToken(); + assert(NameToken.Kind == TokenInfo::TK_Ident); + const TokenInfo OpenToken = Tokenizer->consumeNextToken(); + if (OpenToken.Kind != TokenInfo::TK_OpenParen) { + Error->pushErrorFrame(OpenToken.Range, Error->ET_ParserNoOpenParen) + << OpenToken.Text; + return false; + } + + std::vector Args; + TokenInfo EndToken; + while (Tokenizer->nextTokenKind() != TokenInfo::TK_Eof) { + if (Tokenizer->nextTokenKind() == TokenInfo::TK_CloseParen) { + // End of args. + EndToken = Tokenizer->consumeNextToken(); + break; + } + if (Args.size() > 0) { + // We must find a , token to continue. + const TokenInfo CommaToken = Tokenizer->consumeNextToken(); + if (CommaToken.Kind != TokenInfo::TK_Comma) { + Error->pushErrorFrame(CommaToken.Range, Error->ET_ParserNoComma) + << CommaToken.Text; + return false; + } + } + + ParserValue ArgValue; + ArgValue.Text = Tokenizer->peekNextToken().Text; + ArgValue.Range = Tokenizer->peekNextToken().Range; + if (!parseExpressionImpl(&ArgValue.Value)) { + Error->pushErrorFrame(NameToken.Range, + Error->ET_ParserMatcherArgFailure) + << (Args.size() + 1) << NameToken.Text; + return false; + } + + Args.push_back(ArgValue); + } + + if (EndToken.Kind == TokenInfo::TK_Eof) { + Error->pushErrorFrame(OpenToken.Range, Error->ET_ParserNoCloseParen); + return false; + } + + // Merge the start and end infos. + SourceRange MatcherRange = NameToken.Range; + MatcherRange.End = EndToken.Range.End; + DynTypedMatcher *Result = + S->actOnMatcherExpression(NameToken.Text, MatcherRange, Args, Error); + if (Result == NULL) { + Error->pushErrorFrame(NameToken.Range, Error->ET_ParserMatcherFailure) + << NameToken.Text; + return false; + } + + Value->takeMatcher(Result); + return true; +} + +/// \brief Parse an +bool Parser::parseExpressionImpl(VariantValue *Value) { + switch (Tokenizer->nextTokenKind()) { + case TokenInfo::TK_Literal: + *Value = Tokenizer->consumeNextToken().Value; + return true; + + case TokenInfo::TK_Ident: + return parseMatcherExpressionImpl(Value); + + case TokenInfo::TK_Eof: + Error->pushErrorFrame(Tokenizer->consumeNextToken().Range, + Error->ET_ParserNoCode); + return false; + + case TokenInfo::TK_Error: + // This error was already reported by the tokenizer. + return false; + + case TokenInfo::TK_OpenParen: + case TokenInfo::TK_CloseParen: + case TokenInfo::TK_Comma: + case TokenInfo::TK_InvalidChar: + const TokenInfo Token = Tokenizer->consumeNextToken(); + Error->pushErrorFrame(Token.Range, Error->ET_ParserInvalidToken) + << Token.Text; + return false; + } + + llvm_unreachable("Unknown token kind."); +} + +Parser::Parser(CodeTokenizer *Tokenizer, Sema *S, + Diagnostics *Error) + : Tokenizer(Tokenizer), S(S), Error(Error) {} + +class RegistrySema : public Parser::Sema { +public: + virtual ~RegistrySema() {} + DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef Args, + Diagnostics *Error) { + return Registry::constructMatcher(MatcherName, NameRange, Args, Error); + } +}; + +bool Parser::parseExpression(StringRef Code, VariantValue *Value, + Diagnostics *Error) { + RegistrySema S; + return parseExpression(Code, &S, Value, Error); +} + +bool Parser::parseExpression(StringRef Code, Sema *S, + VariantValue *Value, Diagnostics *Error) { + CodeTokenizer Tokenizer(Code, Error); + return Parser(&Tokenizer, S, Error).parseExpressionImpl(Value); +} + +DynTypedMatcher *Parser::parseMatcherExpression(StringRef Code, + Diagnostics *Error) { + RegistrySema S; + return parseMatcherExpression(Code, &S, Error); +} + +DynTypedMatcher *Parser::parseMatcherExpression(StringRef Code, + Parser::Sema *S, + Diagnostics *Error) { + VariantValue Value; + if (!parseExpression(Code, S, &Value, Error)) + return NULL; + if (!Value.isMatcher()) { + Error->pushErrorFrame(SourceRange(), Error->ET_ParserNotAMatcher); + return NULL; + } + return Value.getMatcher().clone(); +} + +} // namespace dynamic +} // namespace ast_matchers +} // namespace clang diff --git a/lib/ASTMatchers/Dynamic/Registry.cpp b/lib/ASTMatchers/Dynamic/Registry.cpp new file mode 100644 index 0000000000..53e90f1c77 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/Registry.cpp @@ -0,0 +1,153 @@ +//===--- Registry.cpp - Matcher registry ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Registry map populated at static initialization time. +/// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/Dynamic/Registry.h" + +#include + +#include "Marshallers.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ManagedStatic.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { +namespace { + +using internal::MatcherCreateCallback; + +typedef llvm::StringMap ConstructorMap; +class RegistryMaps { +public: + RegistryMaps(); + ~RegistryMaps(); + + const ConstructorMap &constructors() const { return Constructors; } + +private: + void registerMatcher(StringRef MatcherName, MatcherCreateCallback *Callback); + ConstructorMap Constructors; +}; + +void RegistryMaps::registerMatcher(StringRef MatcherName, + MatcherCreateCallback *Callback) { + Constructors[MatcherName] = Callback; +} + +#define REGISTER_MATCHER(name) \ + registerMatcher(#name, internal::makeMatcherAutoMarshall( \ + ::clang::ast_matchers::name, #name)); + +/// \brief Generate a registry map with all the known matchers. +RegistryMaps::RegistryMaps() { + // TODO: This list is not complete. It only has non-overloaded matchers, + // which are the simplest to add to the system. Overloaded matchers require + // more supporting code that was omitted from the first revision for + // simplicitly of code review. + + REGISTER_MATCHER(binaryOperator); + REGISTER_MATCHER(bindTemporaryExpr); + REGISTER_MATCHER(boolLiteral); + REGISTER_MATCHER(callExpr); + REGISTER_MATCHER(characterLiteral); + REGISTER_MATCHER(compoundStmt); + REGISTER_MATCHER(conditionalOperator); + REGISTER_MATCHER(constCastExpr); + REGISTER_MATCHER(constructExpr); + REGISTER_MATCHER(constructorDecl); + REGISTER_MATCHER(declRefExpr); + REGISTER_MATCHER(declStmt); + REGISTER_MATCHER(defaultArgExpr); + REGISTER_MATCHER(doStmt); + REGISTER_MATCHER(dynamicCastExpr); + REGISTER_MATCHER(explicitCastExpr); + REGISTER_MATCHER(expr); + REGISTER_MATCHER(fieldDecl); + REGISTER_MATCHER(forStmt); + REGISTER_MATCHER(functionDecl); + REGISTER_MATCHER(hasAnyParameter); + REGISTER_MATCHER(hasAnySubstatement); + REGISTER_MATCHER(hasConditionVariableStatement); + REGISTER_MATCHER(hasDestinationType); + REGISTER_MATCHER(hasEitherOperand); + REGISTER_MATCHER(hasFalseExpression); + REGISTER_MATCHER(hasImplicitDestinationType); + REGISTER_MATCHER(hasInitializer); + REGISTER_MATCHER(hasLHS); + REGISTER_MATCHER(hasName); + REGISTER_MATCHER(hasObjectExpression); + REGISTER_MATCHER(hasRHS); + REGISTER_MATCHER(hasSourceExpression); + REGISTER_MATCHER(hasTrueExpression); + REGISTER_MATCHER(hasUnaryOperand); + REGISTER_MATCHER(ifStmt); + REGISTER_MATCHER(implicitCastExpr); + REGISTER_MATCHER(integerLiteral); + REGISTER_MATCHER(isArrow); + REGISTER_MATCHER(isConstQualified); + REGISTER_MATCHER(isImplicit); + REGISTER_MATCHER(member); + REGISTER_MATCHER(memberExpr); + REGISTER_MATCHER(methodDecl); + REGISTER_MATCHER(namedDecl); + REGISTER_MATCHER(newExpr); + REGISTER_MATCHER(ofClass); + REGISTER_MATCHER(on); + REGISTER_MATCHER(onImplicitObjectArgument); + REGISTER_MATCHER(operatorCallExpr); + REGISTER_MATCHER(recordDecl); + REGISTER_MATCHER(reinterpretCastExpr); + REGISTER_MATCHER(staticCastExpr); + REGISTER_MATCHER(stmt); + REGISTER_MATCHER(stringLiteral); + REGISTER_MATCHER(switchCase); + REGISTER_MATCHER(to); + REGISTER_MATCHER(unaryOperator); + REGISTER_MATCHER(varDecl); + REGISTER_MATCHER(whileStmt); +} + +RegistryMaps::~RegistryMaps() { + for (ConstructorMap::iterator it = Constructors.begin(), + end = Constructors.end(); + it != end; ++it) { + delete it->second; + } +} + +static llvm::ManagedStatic RegistryData; + +} // anonymous namespace + +// static +DynTypedMatcher *Registry::constructMatcher(StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef Args, + Diagnostics *Error) { + ConstructorMap::const_iterator it = + RegistryData->constructors().find(MatcherName); + if (it == RegistryData->constructors().end()) { + Error->pushErrorFrame(NameRange, Error->ET_RegistryNotFound) + << MatcherName; + return NULL; + } + + return it->second->run(NameRange, Args, Error); +} + +} // namespace dynamic +} // namespace ast_matchers +} // namespace clang diff --git a/lib/ASTMatchers/Dynamic/VariantValue.cpp b/lib/ASTMatchers/Dynamic/VariantValue.cpp new file mode 100644 index 0000000000..e310fbfc58 --- /dev/null +++ b/lib/ASTMatchers/Dynamic/VariantValue.cpp @@ -0,0 +1,105 @@ +//===--- VariantValue.cpp - Polymorphic value type -*- C++ -*-===/ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Polymorphic value type. +/// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/Dynamic/VariantValue.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { + +VariantValue::VariantValue(const VariantValue &Other) : Type(VT_Nothing) { + *this = Other; +} + +VariantValue::VariantValue(const DynTypedMatcher &Matcher) : Type(VT_Nothing) { + setMatcher(Matcher); +} + +VariantValue::VariantValue(const std::string &String) : Type(VT_Nothing) { + setString(String); +} + +VariantValue::~VariantValue() { reset(); } + +VariantValue &VariantValue::operator=(const VariantValue &Other) { + if (this == &Other) return *this; + reset(); + switch (Other.Type) { + case VT_String: + setString(Other.getString()); + break; + case VT_Matcher: + setMatcher(Other.getMatcher()); + break; + case VT_Nothing: + Type = VT_Nothing; + break; + } + return *this; +} + +void VariantValue::reset() { + switch (Type) { + case VT_String: + delete Value.String; + break; + case VT_Matcher: + delete Value.Matcher; + break; + // Cases that do nothing. + case VT_Nothing: + break; + } + Type = VT_Nothing; +} + +bool VariantValue::isString() const { + return Type == VT_String; +} + +const std::string &VariantValue::getString() const { + assert(isString()); + return *Value.String; +} + +void VariantValue::setString(const std::string &NewValue) { + reset(); + Type = VT_String; + Value.String = new std::string(NewValue); +} + +bool VariantValue::isMatcher() const { + return Type == VT_Matcher; +} + +const DynTypedMatcher &VariantValue::getMatcher() const { + assert(isMatcher()); + return *Value.Matcher; +} + +void VariantValue::setMatcher(const DynTypedMatcher &NewValue) { + reset(); + Type = VT_Matcher; + Value.Matcher = NewValue.clone(); +} + +void VariantValue::takeMatcher(DynTypedMatcher *NewValue) { + reset(); + Type = VT_Matcher; + Value.Matcher = NewValue; +} + +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang diff --git a/lib/ASTMatchers/Makefile b/lib/ASTMatchers/Makefile index 76d82719a1..3ee9ccb580 100644 --- a/lib/ASTMatchers/Makefile +++ b/lib/ASTMatchers/Makefile @@ -10,4 +10,6 @@ CLANG_LEVEL := ../.. LIBRARYNAME := clangASTMatchers +PARALLEL_DIRS = Dynamic + include $(CLANG_LEVEL)/Makefile diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h index 5fed85bb30..05258f7fe2 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.h +++ b/unittests/ASTMatchers/ASTMatchersTest.h @@ -84,6 +84,41 @@ testing::AssertionResult notMatches(const std::string &Code, return matchesConditionally(Code, AMatcher, false, "-std=c++11"); } +inline testing::AssertionResult +matchesConditionallyDynamic(const std::string &Code, + const internal::DynTypedMatcher &AMatcher, + bool ExpectMatch, llvm::StringRef CompileArg) { + bool Found = false; + MatchFinder Finder; + Finder.addDynamicMatcher(AMatcher, new VerifyMatch(0, &Found)); + OwningPtr Factory(newFrontendActionFactory(&Finder)); + // Some tests use typeof, which is a gnu extension. + std::vector Args(1, CompileArg); + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { + return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; + } + if (!Found && ExpectMatch) { + return testing::AssertionFailure() + << "Could not find match in \"" << Code << "\""; + } else if (Found && !ExpectMatch) { + return testing::AssertionFailure() + << "Found unexpected match in \"" << Code << "\""; + } + return testing::AssertionSuccess(); +} + +inline testing::AssertionResult +matchesDynamic(const std::string &Code, + const internal::DynTypedMatcher &AMatcher) { + return matchesConditionallyDynamic(Code, AMatcher, true, "-std=c++11"); +} + +inline testing::AssertionResult +notMatchesDynamic(const std::string &Code, + const internal::DynTypedMatcher &AMatcher) { + return matchesConditionallyDynamic(Code, AMatcher, false, "-std=c++11"); +} + template testing::AssertionResult matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher, diff --git a/unittests/ASTMatchers/CMakeLists.txt b/unittests/ASTMatchers/CMakeLists.txt index 91feaac4d9..862c6a0fd9 100644 --- a/unittests/ASTMatchers/CMakeLists.txt +++ b/unittests/ASTMatchers/CMakeLists.txt @@ -11,3 +11,5 @@ add_clang_unittest(ASTMatchersTests target_link_libraries(ASTMatchersTests gtest gtest_main clangASTMatchers clangTooling) + +add_subdirectory(Dynamic) diff --git a/unittests/ASTMatchers/Dynamic/CMakeLists.txt b/unittests/ASTMatchers/Dynamic/CMakeLists.txt new file mode 100644 index 0000000000..eb9fa549e1 --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/CMakeLists.txt @@ -0,0 +1,7 @@ +add_clang_unittest(DynamicASTMatchersTests + VariantValueTest.cpp + ParserTest.cpp + RegistryTest.cpp) + +target_link_libraries(DynamicASTMatchersTests + gtest gtest_main clangASTMatchers clangDynamicASTMatchers clangTooling) diff --git a/unittests/ASTMatchers/Dynamic/Makefile b/unittests/ASTMatchers/Dynamic/Makefile new file mode 100644 index 0000000000..082a4c04c1 --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/Makefile @@ -0,0 +1,18 @@ +##===- unittests/ASTMatchers/Dynamic/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL = ../../.. +TESTNAME = DynamicASTMatchers +LINK_COMPONENTS := support mc +USEDLIBS = clangEdit.a clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ + clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \ + clangAST.a clangASTMatchers.a clangLex.a clangBasic.a \ + clangDynamicASTMatchers.a + +include $(CLANG_LEVEL)/unittests/Makefile diff --git a/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/unittests/ASTMatchers/Dynamic/ParserTest.cpp new file mode 100644 index 0000000000..41f522856d --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -0,0 +1,194 @@ +//===- unittest/ASTMatchers/Dynamic/ParserTest.cpp - Parser unit tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-------------------------------------------------------------------===// + +#include +#include + +#include "../ASTMatchersTest.h" +#include "clang/ASTMatchers/Dynamic/Parser.h" +#include "clang/ASTMatchers/Dynamic/Registry.h" +#include "gtest/gtest.h" +#include "llvm/ADT/StringMap.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { +namespace { + +class DummyDynTypedMatcher : public DynTypedMatcher { +public: + DummyDynTypedMatcher(uint64_t ID) : ID(ID) {} + + typedef ast_matchers::internal::ASTMatchFinder ASTMatchFinder; + typedef ast_matchers::internal::BoundNodesTreeBuilder BoundNodesTreeBuilder; + virtual bool matches(const ast_type_traits::DynTypedNode DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return false; + } + + /// \brief Makes a copy of this matcher object. + virtual DynTypedMatcher *clone() const { + return new DummyDynTypedMatcher(ID); + } + + /// \brief Returns a unique ID for the matcher. + virtual uint64_t getID() const { return ID; } + +private: + uint64_t ID; +}; + +class MockSema : public Parser::Sema { +public: + virtual ~MockSema() {} + + uint64_t expectMatcher(StringRef MatcherName) { + uint64_t ID = ExpectedMatchers.size() + 1; + ExpectedMatchers[MatcherName] = ID; + return ID; + } + + void parse(StringRef Code) { + Diagnostics Error; + VariantValue Value; + Parser::parseExpression(Code, this, &Value, &Error); + Values.push_back(Value); + Errors.push_back(Error.ToStringFull()); + } + + DynTypedMatcher *actOnMatcherExpression(StringRef MatcherName, + const SourceRange &NameRange, + ArrayRef Args, + Diagnostics *Error) { + MatcherInfo ToStore = { MatcherName, NameRange, Args }; + Matchers.push_back(ToStore); + return new DummyDynTypedMatcher(ExpectedMatchers[MatcherName]); + } + + struct MatcherInfo { + StringRef MatcherName; + SourceRange NameRange; + std::vector Args; + }; + + std::vector Errors; + std::vector Values; + std::vector Matchers; + llvm::StringMap ExpectedMatchers; +}; + +TEST(ParserTest, ParseString) { + MockSema Sema; + Sema.parse("\"Foo\""); + Sema.parse("\"\""); + Sema.parse("\"Baz"); + EXPECT_EQ(3ULL, Sema.Values.size()); + EXPECT_EQ("Foo", Sema.Values[0].getString()); + EXPECT_EQ("", Sema.Values[1].getString()); + EXPECT_EQ("1:1: Error parsing string token: <\"Baz>", Sema.Errors[2]); +} + +bool matchesRange(const SourceRange &Range, unsigned StartLine, + unsigned EndLine, unsigned StartColumn, unsigned EndColumn) { + EXPECT_EQ(StartLine, Range.Start.Line); + EXPECT_EQ(EndLine, Range.End.Line); + EXPECT_EQ(StartColumn, Range.Start.Column); + EXPECT_EQ(EndColumn, Range.End.Column); + return Range.Start.Line == StartLine && Range.End.Line == EndLine && + Range.Start.Column == StartColumn && Range.End.Column == EndColumn; +} + +TEST(ParserTest, ParseMatcher) { + MockSema Sema; + const uint64_t ExpectedFoo = Sema.expectMatcher("Foo"); + const uint64_t ExpectedBar = Sema.expectMatcher("Bar"); + const uint64_t ExpectedBaz = Sema.expectMatcher("Baz"); + Sema.parse(" Foo ( Bar (), Baz( \n \"B A,Z\") ) "); + for (size_t i = 0, e = Sema.Errors.size(); i != e; ++i) { + EXPECT_EQ("", Sema.Errors[i]); + } + + EXPECT_EQ(1ULL, Sema.Values.size()); + EXPECT_EQ(ExpectedFoo, Sema.Values[0].getMatcher().getID()); + + EXPECT_EQ(3ULL, Sema.Matchers.size()); + const MockSema::MatcherInfo Bar = Sema.Matchers[0]; + EXPECT_EQ("Bar", Bar.MatcherName); + EXPECT_TRUE(matchesRange(Bar.NameRange, 1, 1, 8, 14)); + EXPECT_EQ(0ULL, Bar.Args.size()); + + const MockSema::MatcherInfo Baz = Sema.Matchers[1]; + EXPECT_EQ("Baz", Baz.MatcherName); + EXPECT_TRUE(matchesRange(Baz.NameRange, 1, 2, 16, 10)); + EXPECT_EQ(1ULL, Baz.Args.size()); + EXPECT_EQ("B A,Z", Baz.Args[0].Value.getString()); + + const MockSema::MatcherInfo Foo = Sema.Matchers[2]; + EXPECT_EQ("Foo", Foo.MatcherName); + EXPECT_TRUE(matchesRange(Foo.NameRange, 1, 2, 2, 12)); + EXPECT_EQ(2ULL, Foo.Args.size()); + EXPECT_EQ(ExpectedBar, Foo.Args[0].Value.getMatcher().getID()); + EXPECT_EQ(ExpectedBaz, Foo.Args[1].Value.getMatcher().getID()); +} + +using ast_matchers::internal::Matcher; + +TEST(ParserTest, FullParserTest) { + OwningPtr Matcher(Parser::parseMatcherExpression( + "hasInitializer(binaryOperator(hasLHS(integerLiteral())))", NULL)); + EXPECT_TRUE(matchesDynamic("int x = 1 + false;", *Matcher)); + EXPECT_FALSE(matchesDynamic("int x = true + 1;", *Matcher)); + + Diagnostics Error; + EXPECT_TRUE(Parser::parseMatcherExpression( + "hasInitializer(\n binaryOperator(hasLHS(\"A\")))", &Error) == NULL); + EXPECT_EQ("1:1: Error parsing argument 1 for matcher hasInitializer.\n" + "2:5: Error parsing argument 1 for matcher binaryOperator.\n" + "2:20: Error building matcher hasLHS.\n" + "2:27: Incorrect type on function hasLHS for arg 1.", + Error.ToStringFull()); +} + +std::string ParseWithError(StringRef Code) { + Diagnostics Error; + VariantValue Value; + Parser::parseExpression(Code, &Value, &Error); + return Error.ToStringFull(); +} + +std::string ParseMatcherWithError(StringRef Code) { + Diagnostics Error; + Parser::parseMatcherExpression(Code, &Error); + return Error.ToStringFull(); +} + +TEST(ParserTest, Errors) { + EXPECT_EQ( + "1:5: Error parsing matcher. Found token <123> while looking for '('.", + ParseWithError("Foo 123")); + EXPECT_EQ( + "1:9: Error parsing matcher. Found token <123> while looking for ','.", + ParseWithError("Foo(\"A\" 123)")); + EXPECT_EQ( + "1:4: Error parsing matcher. Found end-of-code while looking for ')'.", + ParseWithError("Foo(")); + EXPECT_EQ("1:1: End of code found while looking for token.", + ParseWithError("")); + EXPECT_EQ("Input value is not a matcher expression.", + ParseMatcherWithError("\"A\"")); + EXPECT_EQ("1:1: Error parsing argument 1 for matcher Foo.\n" + "1:5: Invalid token <(> found when looking for a value.", + ParseWithError("Foo((")); +} + +} // end anonymous namespace +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang diff --git a/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp new file mode 100644 index 0000000000..64af120193 --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp @@ -0,0 +1,112 @@ +//===- unittest/ASTMatchers/Dynamic/RegistryTest.cpp - Registry unit tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===// + +#include + +#include "../ASTMatchersTest.h" +#include "clang/ASTMatchers/Dynamic/Registry.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { +namespace { + +using ast_matchers::internal::Matcher; + +DynTypedMatcher *constructMatcher(StringRef MatcherName, Diagnostics *Error) { + const std::vector Args; + return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error); +} + +DynTypedMatcher *constructMatcher(StringRef MatcherName, + const VariantValue &Arg1, + Diagnostics *Error) { + std::vector Args(1); + Args[0].Value = Arg1; + return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error); +} + +DynTypedMatcher *constructMatcher(StringRef MatcherName, + const VariantValue &Arg1, + const VariantValue &Arg2, + Diagnostics *Error) { + std::vector Args(2); + Args[0].Value = Arg1; + Args[1].Value = Arg2; + return Registry::constructMatcher(MatcherName, SourceRange(), Args, Error); +} + +TEST(RegistryTest, CanConstructNoArgs) { + OwningPtr IsArrowValue(constructMatcher("isArrow", NULL)); + OwningPtr BoolValue(constructMatcher("boolLiteral", NULL)); + + const std::string ClassSnippet = "struct Foo { int x; };\n" + "Foo *foo = new Foo;\n" + "int i = foo->x;\n"; + const std::string BoolSnippet = "bool Foo = true;\n"; + + EXPECT_TRUE(matchesDynamic(ClassSnippet, *IsArrowValue)); + EXPECT_TRUE(matchesDynamic(BoolSnippet, *BoolValue)); + EXPECT_FALSE(matchesDynamic(ClassSnippet, *BoolValue)); + EXPECT_FALSE(matchesDynamic(BoolSnippet, *IsArrowValue)); +} + +TEST(RegistryTest, ConstructWithSimpleArgs) { + OwningPtr Value( + constructMatcher("hasName", std::string("X"), NULL)); + EXPECT_TRUE(matchesDynamic("class X {};", *Value)); + EXPECT_FALSE(matchesDynamic("int x;", *Value)); +} + +TEST(RegistryTest, ConstructWithMatcherArgs) { + OwningPtr HasInitializerSimple( + constructMatcher("hasInitializer", stmt(), NULL)); + OwningPtr HasInitializerComplex( + constructMatcher("hasInitializer", callExpr(), NULL)); + + std::string code = "int i;"; + EXPECT_FALSE(matchesDynamic(code, *HasInitializerSimple)); + EXPECT_FALSE(matchesDynamic(code, *HasInitializerComplex)); + + code = "int i = 1;"; + EXPECT_TRUE(matchesDynamic(code, *HasInitializerSimple)); + EXPECT_FALSE(matchesDynamic(code, *HasInitializerComplex)); + + code = "int y(); int i = y();"; + EXPECT_TRUE(matchesDynamic(code, *HasInitializerSimple)); + EXPECT_TRUE(matchesDynamic(code, *HasInitializerComplex)); +} + +TEST(RegistryTest, Errors) { + // Incorrect argument count. + OwningPtr Error(new Diagnostics()); + EXPECT_TRUE(NULL == constructMatcher("hasInitializer", Error.get())); + EXPECT_EQ("Incorrect argument count. (Expected = 1) != (Actual = 0)", + Error->ToString()); + Error.reset(new Diagnostics()); + EXPECT_TRUE(NULL == constructMatcher("isArrow", std::string(), Error.get())); + EXPECT_EQ("Incorrect argument count. (Expected = 0) != (Actual = 1)", + Error->ToString()); + + // Bad argument type + Error.reset(new Diagnostics()); + EXPECT_TRUE(NULL == constructMatcher("ofClass", std::string(), Error.get())); + EXPECT_EQ("Incorrect type on function ofClass for arg 1.", Error->ToString()); + Error.reset(new Diagnostics()); + EXPECT_TRUE(NULL == constructMatcher("recordDecl", recordDecl(), + ::std::string(), Error.get())); + EXPECT_EQ("Incorrect type on function recordDecl for arg 2.", + Error->ToString()); +} + +} // end anonymous namespace +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang diff --git a/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp new file mode 100644 index 0000000000..6c202e52fa --- /dev/null +++ b/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp @@ -0,0 +1,97 @@ +//===- unittest/ASTMatchers/Dynamic/VariantValueTest.cpp - VariantValue unit tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------------===// + +#include "../ASTMatchersTest.h" +#include "clang/ASTMatchers/Dynamic/VariantValue.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ast_matchers { +namespace dynamic { +namespace { + +using ast_matchers::internal::DynTypedMatcher; +using ast_matchers::internal::Matcher; + +TEST(VariantValueTest, String) { + const ::std::string kString = "string"; + VariantValue Value = kString; + + EXPECT_TRUE(Value.isString()); + EXPECT_EQ(kString, Value.getString()); + + EXPECT_FALSE(Value.isMatcher()); + EXPECT_FALSE(Value.isTypedMatcher()); + EXPECT_FALSE(Value.isTypedMatcher()); +} + +TEST(VariantValueTest, DynTypedMatcher) { + VariantValue Value = stmt(); + + EXPECT_FALSE(Value.isString()); + + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + + // Conversion to any type of matcher works. + // If they are not compatible it would just return a matcher that matches + // nothing. We test this below. + Value = recordDecl(); + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + + Value = unaryOperator(); + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); +} + +TEST(VariantValueTest, Assignment) { + VariantValue Value = std::string("A"); + EXPECT_TRUE(Value.isString()); + EXPECT_EQ("A", Value.getString()); + EXPECT_FALSE(Value.isMatcher()); + + Value = recordDecl(); + EXPECT_FALSE(Value.isString()); + EXPECT_TRUE(Value.isMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + EXPECT_TRUE(Value.isTypedMatcher()); + + Value = VariantValue(); + EXPECT_FALSE(Value.isString()); + EXPECT_FALSE(Value.isMatcher()); +} + +TEST(GeneicValueTest, Matcher) { + EXPECT_TRUE(matchesDynamic( + "class X {};", VariantValue(recordDecl(hasName("X"))).getMatcher())); + EXPECT_TRUE(matchesDynamic( + "int x;", VariantValue(varDecl()).getTypedMatcher())); + EXPECT_TRUE(matchesDynamic("int foo() { return 1 + 1; }", + VariantValue(functionDecl()).getMatcher())); + // Going through the wrong Matcher will fail to match, even if the + // underlying matcher is correct. + EXPECT_FALSE(matchesDynamic( + "int x;", VariantValue(varDecl()).getTypedMatcher())); + + EXPECT_FALSE( + matchesDynamic("int x;", VariantValue(functionDecl()).getMatcher())); + EXPECT_FALSE(matchesDynamic( + "int foo() { return 1 + 1; }", + VariantValue(declRefExpr()).getTypedMatcher())); +} + +} // end anonymous namespace +} // end namespace dynamic +} // end namespace ast_matchers +} // end namespace clang diff --git a/unittests/ASTMatchers/Makefile b/unittests/ASTMatchers/Makefile index 2abe6eeea5..7ff42f850c 100644 --- a/unittests/ASTMatchers/Makefile +++ b/unittests/ASTMatchers/Makefile @@ -9,6 +9,8 @@ CLANG_LEVEL = ../.. +PARALLEL_DIRS = Dynamic + TESTNAME = ASTMatchers include $(CLANG_LEVEL)/../../Makefile.config LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc -- cgit v1.2.3 From 1fb8d8814ae28a4ebe94652faafad8545a115bfe Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Tue, 14 May 2013 09:30:02 +0000 Subject: Fix clang-format bug in unwrapped-line merging. Before (in styles that allow it), clang-format would not merge an if statement onto a single line, if only the second line was format (e.g. in an editor integration): if (a) return; // clang-format invoked on this line. With this patch, this gets properly merged to: if (a) return; // ... git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181770 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 28 +++++++++++++--------------- unittests/Format/FormatTest.cpp | 10 ++++++---- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index b8e0455c71..2bab6e7353 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -44,7 +44,7 @@ struct ScalarEnumerationTraits { } }; -template<> +template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, clang::format::FormatStyle::BraceBreakingStyle &Value) { @@ -1193,9 +1193,15 @@ public: (touchesLine(TheLine) || touchesPPDirective(I + 1, E))) FormatPPDirective = true; + // Determine indent and try to merge multiple unwrapped lines. while (IndentForLevel.size() <= TheLine.Level) IndentForLevel.push_back(-1); IndentForLevel.resize(TheLine.Level + 1); + unsigned Indent = getIndent(IndentForLevel, TheLine.Level); + if (static_cast(Indent) + Offset >= 0) + Indent += Offset; + tryFitMultipleLinesInOne(Indent, I, E); + bool WasMoved = PreviousLineWasTouched && FirstTok.NewlinesBefore == 0; if (TheLine.First.is(tok::eof)) { if (PreviousLineWasTouched) { @@ -1206,9 +1212,6 @@ public: } else if (TheLine.Type != LT_Invalid && (WasMoved || FormatPPDirective || touchesLine(TheLine))) { unsigned LevelIndent = getIndent(IndentForLevel, TheLine.Level); - unsigned Indent = LevelIndent; - if (static_cast(Indent) + Offset >= 0) - Indent += Offset; if (FirstTok.WhiteSpaceStart.isValid() && // Insert a break even if there is a structural error in case where // we break apart a line consisting of multiple unwrapped lines. @@ -1219,7 +1222,6 @@ public: Indent = LevelIndent = SourceMgr.getSpellingColumnNumber(FirstTok.Tok.getLocation()) - 1; } - tryFitMultipleLinesInOne(Indent, I, E); UnwrappedLineFormatter Formatter(Style, SourceMgr, TheLine, Indent, TheLine.First, Whitespaces); PreviousEndOfLineColumn = @@ -1228,18 +1230,17 @@ public: PreviousLineWasTouched = true; } else { if (FirstTok.NewlinesBefore > 0 || FirstTok.IsFirst) { - unsigned Indent = + unsigned LevelIndent = SourceMgr.getSpellingColumnNumber(FirstTok.Tok.getLocation()) - 1; - unsigned LevelIndent = Indent; + // Remove trailing whitespace of the previous line if it was touched. + if (PreviousLineWasTouched || touchesEmptyLineBefore(TheLine)) + formatFirstToken(TheLine.First, PreviousLineLastToken, LevelIndent, + TheLine.InPPDirective, PreviousEndOfLineColumn); + if (static_cast(LevelIndent) - Offset >= 0) LevelIndent -= Offset; if (TheLine.First.isNot(tok::comment)) IndentForLevel[TheLine.Level] = LevelIndent; - - // Remove trailing whitespace of the previous line if it was touched. - if (PreviousLineWasTouched || touchesEmptyLineBefore(TheLine)) - formatFirstToken(TheLine.First, PreviousLineLastToken, Indent, - TheLine.InPPDirective, PreviousEndOfLineColumn); } // If we did not reformat this unwrapped line, the column at the end of // the last token is unchanged - thus, we can calculate the end of the @@ -1327,8 +1328,6 @@ private: /// /// This will change \c Line and \c AnnotatedLine to contain the merged line, /// if possible; note that \c I will be incremented when lines are merged. - /// - /// Returns whether the resulting \c Line can fit in a single line. void tryFitMultipleLinesInOne(unsigned Indent, std::vector::iterator &I, std::vector::iterator E) { @@ -1352,7 +1351,6 @@ private: I->First.FormatTok.IsFirst)) { tryMergeSimplePPDirective(I, E, Limit); } - return; } void tryMergeSimplePPDirective(std::vector::iterator &I, diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index b7f544e8a1..19c5215a3f 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -245,6 +245,8 @@ TEST_F(FormatTest, FormatIfWithoutCompountStatement) { "}", AllowsMergedIf); + EXPECT_EQ("if (a) return;", format("if(a)\nreturn;", 7, 1, AllowsMergedIf)); + AllowsMergedIf.ColumnLimit = 14; verifyFormat("if (a) return;", AllowsMergedIf); verifyFormat("if (aaaaaaaaa)\n" @@ -3810,7 +3812,7 @@ TEST_F(FormatTest, ReformatRegionAdjustsIndent) { "a;\n" "}\n" "{\n" - " b;\n" + " b; //\n" "}\n" "}", format("{\n" @@ -3818,15 +3820,15 @@ TEST_F(FormatTest, ReformatRegionAdjustsIndent) { "a;\n" "}\n" "{\n" - " b;\n" + " b; //\n" "}\n" "}", 22, 2, getLLVMStyle())); EXPECT_EQ(" {\n" - " a;\n" + " a; //\n" " }", format(" {\n" - "a;\n" + "a; //\n" " }", 4, 2, getLLVMStyle())); EXPECT_EQ("void f() {}\n" -- cgit v1.2.3 From 84f5ddfacc59c5d4878cd47b7794e53dc8e2e9f9 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Tue, 14 May 2013 10:31:09 +0000 Subject: Correctly determine ranges for clang-format. We have been assuming that CharSourceRange::getTokenRange() by itself expands a range until the end of a token, but in fact it only sets IsTokenRange to true. Thus, we have so far only considered the first character of the last token to belong to an unwrapped line. This did not really manifest in symptoms as all edit integrations expand ranges to fully lines. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181778 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 4 ++-- unittests/Format/FormatTest.cpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 2bab6e7353..5cb5fef9d5 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -1473,9 +1473,9 @@ private: bool touchesLine(const AnnotatedLine &TheLine) { const FormatToken *First = &TheLine.First.FormatTok; const FormatToken *Last = &TheLine.Last->FormatTok; - CharSourceRange LineRange = CharSourceRange::getTokenRange( + CharSourceRange LineRange = CharSourceRange::getCharRange( First->WhiteSpaceStart.getLocWithOffset(First->LastNewlineOffset), - Last->Tok.getLocation()); + Last->Tok.getLocation().getLocWithOffset(Last->TokenLength - 1)); return touchesRanges(LineRange); } diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 19c5215a3f..665f20322e 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -218,7 +218,7 @@ TEST_F(FormatTest, ReformatsMovedLines) { // Tests for control statements. //===----------------------------------------------------------------------===// -TEST_F(FormatTest, FormatIfWithoutCompountStatement) { +TEST_F(FormatTest, FormatIfWithoutCompoundStatement) { verifyFormat("if (true)\n f();\ng();"); verifyFormat("if (a)\n if (b)\n if (c)\n g();\nh();"); verifyFormat("if (a)\n if (b) {\n f();\n }\ng();"); @@ -246,6 +246,8 @@ TEST_F(FormatTest, FormatIfWithoutCompountStatement) { AllowsMergedIf); EXPECT_EQ("if (a) return;", format("if(a)\nreturn;", 7, 1, AllowsMergedIf)); + EXPECT_EQ("if (a) return; // comment", + format("if(a)\nreturn; // comment", 20, 1, AllowsMergedIf)); AllowsMergedIf.ColumnLimit = 14; verifyFormat("if (a) return;", AllowsMergedIf); -- cgit v1.2.3 From 089f78d9cc1192244c9884e0e60dd82d415d8138 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Tue, 14 May 2013 10:44:17 +0000 Subject: Fix uninitialized value bug found by valgrind. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181779 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/TokenAnnotator.cpp | 2 -- lib/Format/TokenAnnotator.h | 9 +++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 0f91b11033..11f81fd8ba 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -874,8 +874,6 @@ void TokenAnnotator::annotate(AnnotatedLine &Line) { Line.First.SpacesRequiredBefore = 1; Line.First.MustBreakBefore = Line.First.FormatTok.MustBreakBefore; Line.First.CanBreakBefore = Line.First.MustBreakBefore; - - Line.First.TotalLength = Line.First.FormatTok.TokenLength; } void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) { diff --git a/lib/Format/TokenAnnotator.h b/lib/Format/TokenAnnotator.h index 5ea30159a2..227dd52396 100644 --- a/lib/Format/TokenAnnotator.h +++ b/lib/Format/TokenAnnotator.h @@ -75,10 +75,11 @@ public: : FormatTok(FormatTok), Type(TT_Unknown), SpacesRequiredBefore(0), CanBreakBefore(false), MustBreakBefore(false), ClosesTemplateDeclaration(false), MatchingParen(NULL), - ParameterCount(0), BindingStrength(0), SplitPenalty(0), - LongestObjCSelectorName(0), DefinesFunctionType(false), Parent(NULL), - FakeRParens(0), LastInChainOfCalls(false), - PartOfMultiVariableDeclStmt(false), NoMoreTokensOnLevel(false) {} + ParameterCount(0), TotalLength(FormatTok.TokenLength), + BindingStrength(0), SplitPenalty(0), LongestObjCSelectorName(0), + DefinesFunctionType(false), Parent(NULL), FakeRParens(0), + LastInChainOfCalls(false), PartOfMultiVariableDeclStmt(false), + NoMoreTokensOnLevel(false) {} bool is(tok::TokenKind Kind) const { return FormatTok.Tok.is(Kind); } -- cgit v1.2.3 From 1ba0d65d14ba08f490d79003988c3eae71762bda Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Tue, 14 May 2013 11:59:20 +0000 Subject: Hopefully fix configure build. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181780 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/ASTMatchers/Dynamic/Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/unittests/ASTMatchers/Dynamic/Makefile b/unittests/ASTMatchers/Dynamic/Makefile index 082a4c04c1..18473782b8 100644 --- a/unittests/ASTMatchers/Dynamic/Makefile +++ b/unittests/ASTMatchers/Dynamic/Makefile @@ -8,11 +8,13 @@ ##===----------------------------------------------------------------------===## CLANG_LEVEL = ../../.. + TESTNAME = DynamicASTMatchers +#include $(CLANG_LEVEL)/../../Makefile.config LINK_COMPONENTS := support mc -USEDLIBS = clangEdit.a clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ - clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \ - clangAST.a clangASTMatchers.a clangLex.a clangBasic.a \ - clangDynamicASTMatchers.a +USEDLIBS = clangEdit.a clangTooling.a clangFrontend.a clangSerialization.a \ + clangDriver.a clangRewrite.a clangParse.a clangSema.a \ + clangAnalysis.a clangAST.a clangASTMatchers.a clangLex.a \ + clangBasic.a clangDynamicASTMatchers.a include $(CLANG_LEVEL)/unittests/Makefile -- cgit v1.2.3 From 9d126fb59cf4aacc8e5058c830f23ba7be1db0fe Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Tue, 14 May 2013 12:21:21 +0000 Subject: Take a stab at trying to unbreak the makefile build. There is no clangRewrite.a. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181781 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGBuiltin.cpp | 4 +++- unittests/ASTMatchers/Dynamic/Makefile | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index d18767897f..cacdf3438d 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -160,7 +160,9 @@ static Value *EmitFAbs(CodeGenFunction &CGF, Value *V, QualType ValTy) { false); llvm::Value *Fn = CGF.CGM.CreateRuntimeFunction(FT, FnName); - return CGF.EmitNounwindRuntimeCall(Fn, V, "abs"); + llvm::CallInst *Call = CGF.EmitNounwindRuntimeCall(Fn, V, "abs"); + Call->setDoesNotAccessMemory(); + return Call; } static RValue emitLibraryCall(CodeGenFunction &CGF, const FunctionDecl *Fn, diff --git a/unittests/ASTMatchers/Dynamic/Makefile b/unittests/ASTMatchers/Dynamic/Makefile index 18473782b8..52a02d0ec5 100644 --- a/unittests/ASTMatchers/Dynamic/Makefile +++ b/unittests/ASTMatchers/Dynamic/Makefile @@ -10,11 +10,11 @@ CLANG_LEVEL = ../../.. TESTNAME = DynamicASTMatchers -#include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := support mc -USEDLIBS = clangEdit.a clangTooling.a clangFrontend.a clangSerialization.a \ - clangDriver.a clangRewrite.a clangParse.a clangSema.a \ - clangAnalysis.a clangAST.a clangASTMatchers.a clangLex.a \ - clangBasic.a clangDynamicASTMatchers.a +include $(CLANG_LEVEL)/../../Makefile.config +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc +USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ + clangRewriteCore.a clangRewriteFrontend.a clangParse.a clangSema.a \ + clangAnalysis.a clangEdit.a clangAST.a clangASTMatchers.a \ + clangLex.a clangBasic.a clangDynamicASTMatchers.a include $(CLANG_LEVEL)/unittests/Makefile -- cgit v1.2.3 From b71757b2e02b83c18b736729a747016f0b1e1353 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Tue, 14 May 2013 12:23:08 +0000 Subject: Revert accidental commit. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181782 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGBuiltin.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index cacdf3438d..d18767897f 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -160,9 +160,7 @@ static Value *EmitFAbs(CodeGenFunction &CGF, Value *V, QualType ValTy) { false); llvm::Value *Fn = CGF.CGM.CreateRuntimeFunction(FT, FnName); - llvm::CallInst *Call = CGF.EmitNounwindRuntimeCall(Fn, V, "abs"); - Call->setDoesNotAccessMemory(); - return Call; + return CGF.EmitNounwindRuntimeCall(Fn, V, "abs"); } static RValue emitLibraryCall(CodeGenFunction &CGF, const FunctionDecl *Fn, -- cgit v1.2.3 From 46a89351e10fb1e058e8b338c32f92cb999885fa Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Tue, 14 May 2013 12:41:50 +0000 Subject: Link dynamic ast matchers with the ast matchers library. Unbreaks shared cmake build. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181783 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/ASTMatchers/Dynamic/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ASTMatchers/Dynamic/CMakeLists.txt b/lib/ASTMatchers/Dynamic/CMakeLists.txt index ba920a4f67..843341b297 100644 --- a/lib/ASTMatchers/Dynamic/CMakeLists.txt +++ b/lib/ASTMatchers/Dynamic/CMakeLists.txt @@ -10,3 +10,7 @@ add_clang_library(clangDynamicASTMatchers add_dependencies(clangDynamicASTMatchers clangASTMatchers ) + +target_link_libraries(clangDynamicASTMatchers + clangASTMatchers + ) -- cgit v1.2.3 From 4537d6e0f9baf2e011a4260e0d7872789b01c3f2 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Tue, 14 May 2013 12:45:47 +0000 Subject: Fix __clear_cache on ARM. Current gcc's produce an error if __clear_cache is anything but __clear_cache(char *a, char *b); It looks like we had just implemented a gcc bug that is now fixed. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181784 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/BuiltinsARM.def | 2 +- lib/CodeGen/CGBuiltin.cpp | 5 ++--- test/CodeGen/arm-clear.c | 17 ++--------------- test/Sema/builtins-arm.c | 18 +++++++++--------- 4 files changed, 14 insertions(+), 28 deletions(-) diff --git a/include/clang/Basic/BuiltinsARM.def b/include/clang/Basic/BuiltinsARM.def index 888e529111..3dbeb0b0ae 100644 --- a/include/clang/Basic/BuiltinsARM.def +++ b/include/clang/Basic/BuiltinsARM.def @@ -15,7 +15,7 @@ // The format of this database matches clang/Basic/Builtins.def. // In libgcc -BUILTIN(__clear_cache, "v.", "") +BUILTIN(__clear_cache, "vc*c*", "") BUILTIN(__builtin_thread_pointer, "v*", "") // Saturating arithmetic diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index d18767897f..bd5afb6d17 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -1645,11 +1645,10 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { if (BuiltinID == ARM::BI__clear_cache) { + assert(E->getNumArgs() == 2 && "__clear_cache takes 2 arguments"); const FunctionDecl *FD = E->getDirectCallee(); - // Oddly people write this call without args on occasion and gcc accepts - // it - it's also marked as varargs in the description file. SmallVector Ops; - for (unsigned i = 0; i < E->getNumArgs(); i++) + for (unsigned i = 0; i < 2; i++) Ops.push_back(EmitScalarExpr(E->getArg(i))); llvm::Type *Ty = CGM.getTypes().ConvertType(FD->getType()); llvm::FunctionType *FTy = cast(Ty); diff --git a/test/CodeGen/arm-clear.c b/test/CodeGen/arm-clear.c index 51506dfed1..8ef3675641 100644 --- a/test/CodeGen/arm-clear.c +++ b/test/CodeGen/arm-clear.c @@ -1,21 +1,8 @@ // REQUIRES: arm-registered-target // RUN: %clang_cc1 -triple armv7-apple-darwin9 -emit-llvm -w -o - %s | FileCheck %s -void clear0(void *ptr) { - // CHECK: clear0 - // CHECK-NOT: load i8** - __clear_cache(); -} - -void clear1(void *ptr) { - // CHECK: clear1 - // CHECK: load i8** - // CHECK-NOT: load i8** - __clear_cache(ptr); -} - -void clear2(void *ptr, void *ptr2) { - // CHECK: clear2 +void clear(void *ptr, void *ptr2) { + // CHECK: clear // CHECK: load i8** // CHECK: load i8** __clear_cache(ptr, ptr2); diff --git a/test/Sema/builtins-arm.c b/test/Sema/builtins-arm.c index 7b48af155e..b516040f64 100644 --- a/test/Sema/builtins-arm.c +++ b/test/Sema/builtins-arm.c @@ -1,15 +1,15 @@ -// RUN: %clang_cc1 -triple armv7 -fsyntax-only -verify -DTEST0 %s -// RUN: %clang_cc1 -triple armv7 -fsyntax-only -verify -DTEST1 %s +// RUN: %clang_cc1 -triple armv7 -fsyntax-only -verify %s // RUN: %clang_cc1 -triple armv7 -target-abi apcs-gnu \ -// RUN: -fsyntax-only -verify -DTEST1 %s +// RUN: -fsyntax-only -verify %s -#ifdef TEST0 -void __clear_cache(char*, char*); -#endif +void f(char *a, char *b) { + __clear_cache(); // expected-error {{too few arguments to function call, expected 2, have 0}} // expected-note {{'__clear_cache' is a builtin with type 'void (char *, char *)}} + __clear_cache(a); // expected-error {{too few arguments to function call, expected 2, have 1}} + __clear_cache(a, b); +} -#ifdef TEST1 -void __clear_cache(void*, void*); -#endif +void __clear_cache(void*, void*); // expected-error {{conflicting types for '__clear_cache'}} +void __clear_cache(char*, char*); #if defined(__ARM_PCS) || defined(__ARM_EABI__) // va_list on ARM AAPCS is struct { void* __ap }. -- cgit v1.2.3 From 4a25a62bdfc18edbb475192d72076b450fd86fbf Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Tue, 14 May 2013 15:48:54 +0000 Subject: Add LeakSanitizer.rst to docs. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181798 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LeakSanitizer.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 docs/LeakSanitizer.rst diff --git a/docs/LeakSanitizer.rst b/docs/LeakSanitizer.rst new file mode 100644 index 0000000000..09d02cfcd5 --- /dev/null +++ b/docs/LeakSanitizer.rst @@ -0,0 +1,28 @@ +================ +LeakSanitizer +================ + +.. contents:: + :local: + +Introduction +============ + +LeakSanitizer is a heap leak detector which is designed to be used on top of +:doc:`AddressSanitizer` / :doc:`MemorySanitizer`, or as a standalone library. +LeakSanitizer is a run-time tool which doesn't require compiler +instrumentation. + +Current status +============== + +LeakSanitizer is a work in progress, currently under development for +x86\_64 Linux. + +More Information +================ + +Design wiki: +`https://code.google.com/p/address-sanitizer/wiki/LeakSanitizerDesignDocument +`_ + -- cgit v1.2.3 From 173a4cc51a416a938885358b9592e629728e7407 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Tue, 14 May 2013 18:06:10 +0000 Subject: Really fix the declaration of __clear_cache. When I tested gcc's behaviour before, I forgot the extern "C", so it would warn when the types *did* match. So in the end * __clear_cache takes two void pointers. * aarch64 was correct before. * libgcc's manual is wrong. * this patch fixes arm. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181810 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/BuiltinsAArch64.def | 2 +- include/clang/Basic/BuiltinsARM.def | 2 +- test/Sema/builtins-aarch64.c | 2 +- test/Sema/builtins-arm.c | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/clang/Basic/BuiltinsAArch64.def b/include/clang/Basic/BuiltinsAArch64.def index 939c080cc5..9e9f6d0875 100644 --- a/include/clang/Basic/BuiltinsAArch64.def +++ b/include/clang/Basic/BuiltinsAArch64.def @@ -15,4 +15,4 @@ // The format of this database matches clang/Basic/Builtins.def. // In libgcc -BUILTIN(__clear_cache, "vc*c*", "") +BUILTIN(__clear_cache, "vv*v*", "") diff --git a/include/clang/Basic/BuiltinsARM.def b/include/clang/Basic/BuiltinsARM.def index 3dbeb0b0ae..fad9007e04 100644 --- a/include/clang/Basic/BuiltinsARM.def +++ b/include/clang/Basic/BuiltinsARM.def @@ -15,7 +15,7 @@ // The format of this database matches clang/Basic/Builtins.def. // In libgcc -BUILTIN(__clear_cache, "vc*c*", "") +BUILTIN(__clear_cache, "vv*v*", "") BUILTIN(__builtin_thread_pointer, "v*", "") // Saturating arithmetic diff --git a/test/Sema/builtins-aarch64.c b/test/Sema/builtins-aarch64.c index cc2a1549f7..b0557532fa 100644 --- a/test/Sema/builtins-aarch64.c +++ b/test/Sema/builtins-aarch64.c @@ -2,7 +2,7 @@ // RUN: %clang_cc1 -triple aarch64-none-linux-gnu -DTEST1 -fsyntax-only -verify %s #ifdef TEST1 -void __clear_cache(char *start, char *end); +void __clear_cache(void *start, void *end); #endif void test_clear_cache_chars(char *start, char *end) { diff --git a/test/Sema/builtins-arm.c b/test/Sema/builtins-arm.c index b516040f64..3ac1da0aa9 100644 --- a/test/Sema/builtins-arm.c +++ b/test/Sema/builtins-arm.c @@ -2,14 +2,14 @@ // RUN: %clang_cc1 -triple armv7 -target-abi apcs-gnu \ // RUN: -fsyntax-only -verify %s -void f(char *a, char *b) { - __clear_cache(); // expected-error {{too few arguments to function call, expected 2, have 0}} // expected-note {{'__clear_cache' is a builtin with type 'void (char *, char *)}} +void f(void *a, void *b) { + __clear_cache(); // expected-error {{too few arguments to function call, expected 2, have 0}} // expected-note {{'__clear_cache' is a builtin with type 'void (void *, void *)}} __clear_cache(a); // expected-error {{too few arguments to function call, expected 2, have 1}} __clear_cache(a, b); } -void __clear_cache(void*, void*); // expected-error {{conflicting types for '__clear_cache'}} -void __clear_cache(char*, char*); +void __clear_cache(char*, char*); // expected-error {{conflicting types for '__clear_cache'}} +void __clear_cache(void*, void*); #if defined(__ARM_PCS) || defined(__ARM_EABI__) // va_list on ARM AAPCS is struct { void* __ap }. -- cgit v1.2.3 From d6a08d16a0c8d98e2491f3ee012dbb46b64038f2 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Tue, 14 May 2013 20:30:42 +0000 Subject: [ms-cxxabi] Mangle in an implicit 'E' for certain types on win64 Most of the complexity of this patch is figuring out which types get the qualifier and which don't. If we implement __ptr32/64, then we should check the qualifier instead of assuming all pointers are 64-bit. This fixes PR13792. Patch by Warren Hunt! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181825 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/MicrosoftMangle.cpp | 90 ++++++++++++++++++---------- lib/AST/RecordLayoutBuilder.cpp | 2 +- test/CodeGenCXX/mangle-ms-arg-qualifiers.cpp | 41 +++++++++++++ test/CodeGenCXX/mangle-ms-templates.cpp | 26 +++++++- test/CodeGenCXX/mangle-ms.cpp | 20 ++++++- 5 files changed, 143 insertions(+), 36 deletions(-) diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index d242cd9335..dbd37f0201 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -22,6 +22,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/Basic/ABI.h" #include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/TargetInfo.h" #include using namespace clang; @@ -58,18 +59,26 @@ class MicrosoftCXXNameMangler { ASTContext &getASTContext() const { return Context.getASTContext(); } + // FIXME: If we add support for __ptr32/64 qualifiers, then we should push + // this check into mangleQualifiers(). + const bool PointersAre64Bit; + public: enum QualifierMangleMode { QMM_Drop, QMM_Mangle, QMM_Escape, QMM_Result }; MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_) : Context(C), Out(Out_), Structor(0), StructorType(-1), + PointersAre64Bit(C.getASTContext().getTargetInfo().getPointerWidth(0) == + 64), UseNameBackReferences(true) { } MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_, const CXXDestructorDecl *D, CXXDtorType Type) : Context(C), Out(Out_), Structor(getStructor(D)), StructorType(Type), + PointersAre64Bit(C.getASTContext().getTargetInfo().getPointerWidth(0) == + 64), UseNameBackReferences(true) { } raw_ostream &getStream() const { return Out; } @@ -1228,32 +1237,36 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T, } void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) { - // ::= A # private: near - // ::= B # private: far - // ::= C # private: static near - // ::= D # private: static far - // ::= E # private: virtual near - // ::= F # private: virtual far - // ::= G # private: thunk near - // ::= H # private: thunk far - // ::= I # protected: near - // ::= J # protected: far - // ::= K # protected: static near - // ::= L # protected: static far - // ::= M # protected: virtual near - // ::= N # protected: virtual far - // ::= O # protected: thunk near - // ::= P # protected: thunk far - // ::= Q # public: near - // ::= R # public: far - // ::= S # public: static near - // ::= T # public: static far - // ::= U # public: virtual near - // ::= V # public: virtual far - // ::= W # public: thunk near - // ::= X # public: thunk far - // ::= Y # global near - // ::= Z # global far + // ::= E? # E designates a 64-bit 'this' + // # pointer. in 64-bit mode *all* + // # 'this' pointers are 64-bit. + // ::= + // ::= A # private: near + // ::= B # private: far + // ::= C # private: static near + // ::= D # private: static far + // ::= E # private: virtual near + // ::= F # private: virtual far + // ::= G # private: thunk near + // ::= H # private: thunk far + // ::= I # protected: near + // ::= J # protected: far + // ::= K # protected: static near + // ::= L # protected: static far + // ::= M # protected: virtual near + // ::= N # protected: virtual far + // ::= O # protected: thunk near + // ::= P # protected: thunk far + // ::= Q # public: near + // ::= R # public: far + // ::= S # public: static near + // ::= T # public: static far + // ::= U # public: virtual near + // ::= V # public: virtual far + // ::= W # public: thunk near + // ::= X # public: thunk far + // ::= Y # global near + // ::= Z # global far if (const CXXMethodDecl *MD = dyn_cast(FD)) { switch (MD->getAccess()) { default: @@ -1281,6 +1294,8 @@ void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) { else Out << 'Q'; } + if (PointersAre64Bit && !MD->isStatic()) + Out << 'E'; } else Out << 'Y'; } @@ -1380,9 +1395,9 @@ void MicrosoftCXXNameMangler::mangleType(const TagType *T) { // ::= // ::= // [Y +] -// # as global -// ::= Q [Y +] -// # as param +// # as global, E is never required +// ::= Q E? [Y +] +// # as param, E is required for 64-bit // It's supposed to be the other way around, but for some strange reason, it // isn't. Today this behavior is retained for the sole purpose of backwards // compatibility. @@ -1394,6 +1409,8 @@ void MicrosoftCXXNameMangler::mangleDecayedArrayType(const ArrayType *T, manglePointerQualifiers(T->getElementType().getQualifiers()); } else { Out << 'Q'; + if (PointersAre64Bit) + Out << 'E'; } mangleType(T->getElementType(), SourceRange()); } @@ -1494,10 +1511,13 @@ void MicrosoftCXXNameMangler::mangleType( } // ::= -// ::= +// ::= E? +// # the E is required for 64-bit non static pointers void MicrosoftCXXNameMangler::mangleType(const PointerType *T, SourceRange Range) { QualType PointeeTy = T->getPointeeType(); + if (PointersAre64Bit && !T->getPointeeType()->isFunctionType()) + Out << 'E'; mangleType(PointeeTy, Range); } void MicrosoftCXXNameMangler::mangleType(const ObjCObjectPointerType *T, @@ -1508,18 +1528,24 @@ void MicrosoftCXXNameMangler::mangleType(const ObjCObjectPointerType *T, } // ::= -// ::= A +// ::= A E? +// # the E is required for 64-bit non static lvalue references void MicrosoftCXXNameMangler::mangleType(const LValueReferenceType *T, SourceRange Range) { Out << 'A'; + if (PointersAre64Bit && !T->getPointeeType()->isFunctionType()) + Out << 'E'; mangleType(T->getPointeeType(), Range); } // ::= -// ::= $$Q +// ::= $$Q E? +// # the E is required for 64-bit non static rvalue references void MicrosoftCXXNameMangler::mangleType(const RValueReferenceType *T, SourceRange Range) { Out << "$$Q"; + if (PointersAre64Bit && !T->getPointeeType()->isFunctionType()) + Out << 'E'; mangleType(T->getPointeeType(), Range); } diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index f27b502c9c..eb39d08424 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -211,7 +211,7 @@ void EmptySubobjectMap::AddSubobjectAtOffset(const CXXRecordDecl *RD, if (!RD->isEmpty()) return; - // If we have empty structures inside an union, we can assign both + // If we have empty structures inside a union, we can assign both // the same offset. Just avoid pushing them twice in the list. ClassVectorTy& Classes = EmptyClassOffsets[Offset]; if (std::find(Classes.begin(), Classes.end(), RD) != Classes.end()) diff --git a/test/CodeGenCXX/mangle-ms-arg-qualifiers.cpp b/test/CodeGenCXX/mangle-ms-arg-qualifiers.cpp index d03ba52649..ed7027d975 100644 --- a/test/CodeGenCXX/mangle-ms-arg-qualifiers.cpp +++ b/test/CodeGenCXX/mangle-ms-arg-qualifiers.cpp @@ -1,123 +1,164 @@ // RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=x86_64-pc-win32 | FileCheck -check-prefix=X64 %s void foo(const unsigned int) {} // CHECK: "\01?foo@@YAXI@Z" +// X64: "\01?foo@@YAXI@Z" void foo(const double) {} // CHECK: "\01?foo@@YAXN@Z" +// X64: "\01?foo@@YAXN@Z" void bar(const volatile double) {} // CHECK: "\01?bar@@YAXN@Z" +// X64: "\01?bar@@YAXN@Z" void foo_pad(char * x) {} // CHECK: "\01?foo_pad@@YAXPAD@Z" +// X64: "\01?foo_pad@@YAXPEAD@Z" void foo_pbd(const char * x) {} // CHECK: "\01?foo_pbd@@YAXPBD@Z" +// X64: "\01?foo_pbd@@YAXPEBD@Z" void foo_pcd(volatile char * x) {} // CHECK: "\01?foo_pcd@@YAXPCD@Z" +// X64: "\01?foo_pcd@@YAXPECD@Z" void foo_qad(char * const x) {} // CHECK: "\01?foo_qad@@YAXQAD@Z" +// X64: "\01?foo_qad@@YAXQEAD@Z" void foo_rad(char * volatile x) {} // CHECK: "\01?foo_rad@@YAXRAD@Z" +// X64: "\01?foo_rad@@YAXREAD@Z" void foo_sad(char * const volatile x) {} // CHECK: "\01?foo_sad@@YAXSAD@Z" +// X64: "\01?foo_sad@@YAXSEAD@Z" void foo_papad(char ** x) {} // CHECK: "\01?foo_papad@@YAXPAPAD@Z" +// X64: "\01?foo_papad@@YAXPEAPEAD@Z" void foo_papbd(char const ** x) {} // CHECK: "\01?foo_papbd@@YAXPAPBD@Z" +// X64: "\01?foo_papbd@@YAXPEAPEBD@Z" void foo_papcd(char volatile ** x) {} // CHECK: "\01?foo_papcd@@YAXPAPCD@Z" +// X64: "\01?foo_papcd@@YAXPEAPECD@Z" void foo_pbqad(char * const* x) {} // CHECK: "\01?foo_pbqad@@YAXPBQAD@Z" +// X64: "\01?foo_pbqad@@YAXPEBQEAD@Z" void foo_pcrad(char * volatile* x) {} // CHECK: "\01?foo_pcrad@@YAXPCRAD@Z" +// X64: "\01?foo_pcrad@@YAXPECREAD@Z" void foo_qapad(char ** const x) {} // CHECK: "\01?foo_qapad@@YAXQAPAD@Z" +// X64: "\01?foo_qapad@@YAXQEAPEAD@Z" void foo_rapad(char ** volatile x) {} // CHECK: "\01?foo_rapad@@YAXRAPAD@Z" +// X64: "\01?foo_rapad@@YAXREAPEAD@Z" void foo_pbqbd(const char * const* x) {} // CHECK: "\01?foo_pbqbd@@YAXPBQBD@Z" +// X64: "\01?foo_pbqbd@@YAXPEBQEBD@Z" void foo_pbqcd(volatile char * const* x) {} // CHECK: "\01?foo_pbqcd@@YAXPBQCD@Z" +// X64: "\01?foo_pbqcd@@YAXPEBQECD@Z" void foo_pcrbd(const char * volatile* x) {} // CHECK: "\01?foo_pcrbd@@YAXPCRBD@Z" +// X64: "\01?foo_pcrbd@@YAXPECREBD@Z" void foo_pcrcd(volatile char * volatile* x) {} // CHECK: "\01?foo_pcrcd@@YAXPCRCD@Z" +// X64: "\01?foo_pcrcd@@YAXPECRECD@Z" void foo_aad(char &x) {} // CHECK: "\01?foo_aad@@YAXAAD@Z" +// X64: "\01?foo_aad@@YAXAEAD@Z" void foo_abd(const char &x) {} // CHECK: "\01?foo_abd@@YAXABD@Z" +// X64: "\01?foo_abd@@YAXAEBD@Z" void foo_aapad(char *&x) {} // CHECK: "\01?foo_aapad@@YAXAAPAD@Z" +// X64: "\01?foo_aapad@@YAXAEAPEAD@Z" void foo_aapbd(const char *&x) {} // CHECK: "\01?foo_aapbd@@YAXAAPBD@Z" +// X64: "\01?foo_aapbd@@YAXAEAPEBD@Z" void foo_abqad(char * const &x) {} // CHECK: "\01?foo_abqad@@YAXABQAD@Z" +// X64: "\01?foo_abqad@@YAXAEBQEAD@Z" void foo_abqbd(const char * const &x) {} // CHECK: "\01?foo_abqbd@@YAXABQBD@Z" +// X64: "\01?foo_abqbd@@YAXAEBQEBD@Z" void foo_aay144h(int (&x)[5][5]) {} // CHECK: "\01?foo_aay144h@@YAXAAY144H@Z" +// X64: "\01?foo_aay144h@@YAXAEAY144H@Z" void foo_aay144cbh(const int (&x)[5][5]) {} // CHECK: "\01?foo_aay144cbh@@YAXAAY144$$CBH@Z" +// X64: "\01?foo_aay144cbh@@YAXAEAY144$$CBH@Z" void foo_qay144h(int (&&x)[5][5]) {} // CHECK: "\01?foo_qay144h@@YAX$$QAY144H@Z" +// X64: "\01?foo_qay144h@@YAX$$QEAY144H@Z" void foo_qay144cbh(const int (&&x)[5][5]) {} // CHECK: "\01?foo_qay144cbh@@YAX$$QAY144$$CBH@Z" +// X64: "\01?foo_qay144cbh@@YAX$$QEAY144$$CBH@Z" void foo_p6ahxz(int x()) {} // CHECK: "\01?foo_p6ahxz@@YAXP6AHXZ@Z" +// X64: "\01?foo_p6ahxz@@YAXP6AHXZ@Z" void foo_a6ahxz(int (&x)()) {} // CHECK: "\01?foo_a6ahxz@@YAXA6AHXZ@Z" +// X64: "\01?foo_a6ahxz@@YAXA6AHXZ@Z" void foo_q6ahxz(int (&&x)()) {} // CHECK: "\01?foo_q6ahxz@@YAX$$Q6AHXZ@Z" +// X64: "\01?foo_q6ahxz@@YAX$$Q6AHXZ@Z" void foo_qay04h(int x[5][5]) {} // CHECK: "\01?foo_qay04h@@YAXQAY04H@Z" +// X64: "\01?foo_qay04h@@YAXQEAY04H@Z" void foo_qay04cbh(const int x[5][5]) {} // CHECK: "\01?foo_qay04cbh@@YAXQAY04$$CBH@Z" +// X64: "\01?foo_qay04cbh@@YAXQEAY04$$CBH@Z" typedef double Vector[3]; void foo(Vector*) {} // CHECK: "\01?foo@@YAXPAY02N@Z" +// X64: "\01?foo@@YAXPEAY02N@Z" void foo(Vector) {} // CHECK: "\01?foo@@YAXQAN@Z" +// X64: "\01?foo@@YAXQEAN@Z" void foo_const(const Vector) {} // CHECK: "\01?foo_const@@YAXQBN@Z" +// X64: "\01?foo_const@@YAXQEBN@Z" void foo_volatile(volatile Vector) {} // CHECK: "\01?foo_volatile@@YAXQCN@Z" +// X64: "\01?foo_volatile@@YAXQECN@Z" void foo(Vector*, const Vector, const double) {} // CHECK: "\01?foo@@YAXPAY02NQBNN@Z" +// X64: "\01?foo@@YAXPEAY02NQEBNN@Z" diff --git a/test/CodeGenCXX/mangle-ms-templates.cpp b/test/CodeGenCXX/mangle-ms-templates.cpp index 10e68248dc..c52b6b4b7a 100644 --- a/test/CodeGenCXX/mangle-ms-templates.cpp +++ b/test/CodeGenCXX/mangle-ms-templates.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -fms-extensions -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=x86_64-pc-win32 | FileCheck -check-prefix X64 %s template class Class { @@ -33,65 +34,87 @@ class BoolTemplate { void template_mangling() { Class c1; // CHECK: call {{.*}} @"\01??0?$Class@VTypename@@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@VTypename@@@@QEAA@XZ" Class c1_const; // CHECK: call {{.*}} @"\01??0?$Class@$$CBVTypename@@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@$$CBVTypename@@@@QEAA@XZ" Class c1_volatile; // CHECK: call {{.*}} @"\01??0?$Class@$$CCVTypename@@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@$$CCVTypename@@@@QEAA@XZ" Class c1_cv; // CHECK: call {{.*}} @"\01??0?$Class@$$CDVTypename@@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@$$CDVTypename@@@@QEAA@XZ" Class > c2; // CHECK: call {{.*}} @"\01??0?$Class@V?$Nested@VTypename@@@@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@V?$Nested@VTypename@@@@@@QEAA@XZ" Class c_intpc; // CHECK: call {{.*}} @"\01??0?$Class@QAH@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@QEAH@@QEAA@XZ" Class c_ft; // CHECK: call {{.*}} @"\01??0?$Class@$$A6AHXZ@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@$$A6AHXZ@@QEAA@XZ" Class c_inti; // CHECK: call {{.*}} @"\01??0?$Class@$$BY0A@H@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@$$BY0A@H@@QEAA@XZ" Class c_int5; // CHECK: call {{.*}} @"\01??0?$Class@$$BY04H@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@$$BY04H@@QEAA@XZ" Class c_intc5; // CHECK: call {{.*}} @"\01??0?$Class@$$BY04$$CBH@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@$$BY04$$CBH@@QEAA@XZ" Class c_intpc5; // CHECK: call {{.*}} @"\01??0?$Class@$$BY04QAH@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$Class@$$BY04QEAH@@QEAA@XZ" BoolTemplate _false; // CHECK: call {{.*}} @"\01??0?$BoolTemplate@$0A@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$BoolTemplate@$0A@@@QEAA@XZ" BoolTemplate _true; // PR13158 _true.Foo(1); // CHECK: call {{.*}} @"\01??0?$BoolTemplate@$00@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$BoolTemplate@$00@@QEAA@XZ" // CHECK: call {{.*}} @"\01??$Foo@H@?$BoolTemplate@$00@@QAEXH@Z" +// X64: call {{.*}} @"\01??$Foo@H@?$BoolTemplate@$00@@QEAAXH@Z" IntTemplate<0> zero; // CHECK: call {{.*}} @"\01??0?$IntTemplate@$0A@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$IntTemplate@$0A@@@QEAA@XZ" IntTemplate<5> five; // CHECK: call {{.*}} @"\01??0?$IntTemplate@$04@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$IntTemplate@$04@@QEAA@XZ" IntTemplate<11> eleven; // CHECK: call {{.*}} @"\01??0?$IntTemplate@$0L@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$IntTemplate@$0L@@@QEAA@XZ" IntTemplate<256> _256; // CHECK: call {{.*}} @"\01??0?$IntTemplate@$0BAA@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$IntTemplate@$0BAA@@@QEAA@XZ" IntTemplate<513> _513; // CHECK: call {{.*}} @"\01??0?$IntTemplate@$0CAB@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$IntTemplate@$0CAB@@@QEAA@XZ" IntTemplate<1026> _1026; // CHECK: call {{.*}} @"\01??0?$IntTemplate@$0EAC@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$IntTemplate@$0EAC@@@QEAA@XZ" IntTemplate<65535> ffff; // CHECK: call {{.*}} @"\01??0?$IntTemplate@$0PPPP@@@QAE@XZ" +// X64: call {{.*}} @"\01??0?$IntTemplate@$0PPPP@@@QEAA@XZ" } namespace space { template const T& foo(const T& l) { return l; } } // CHECK: "\01??$foo@H@space@@YAABHABH@Z" +// X64: "\01??$foo@H@space@@YAAEBHAEBH@Z" void use() { space::foo(42); @@ -108,4 +131,5 @@ void FunctionPointerTemplate() { void spam() { FunctionPointerTemplate(); // CHECK: "\01??$FunctionPointerTemplate@$1?spam@@YAXXZ@@YAXXZ" +// X64: "\01??$FunctionPointerTemplate@$1?spam@@YAXXZ@@YAXXZ" } diff --git a/test/CodeGenCXX/mangle-ms.cpp b/test/CodeGenCXX/mangle-ms.cpp index 1b98a84823..3f80e54f43 100644 --- a/test/CodeGenCXX/mangle-ms.cpp +++ b/test/CodeGenCXX/mangle-ms.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fms-extensions -fblocks -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s -// RUN: %clang_cc1 -fms-compatibility -fblocks -emit-llvm %s -o - -cxx-abi microsoft -triple=x86_64-pc-win32 | FileCheck -check-prefix X64 %s +// RUN: %clang_cc1 -fblocks -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -fblocks -emit-llvm %s -o - -cxx-abi microsoft -triple=x86_64-pc-win32 | FileCheck -check-prefix X64 %s // CHECK: @"\01?a@@3HA" // CHECK: @"\01?b@N@@3HA" @@ -33,6 +33,7 @@ namespace N { static int c; int _c(void) {return N::anonymous + c;} // CHECK: @"\01?_c@@YAHXZ" +// X64: @"\01?_c@@YAHXZ" class foo { static const short d; @@ -43,15 +44,19 @@ public: int operator+(int a); foo(){} //CHECK: @"\01??0foo@@QAE@XZ" +//X64: @"\01??0foo@@QEAA@XZ" ~foo(){} //CHECK: @"\01??1foo@@QAE@XZ" +//X64: @"\01??1foo@@QEAA@XZ foo(int i){} //CHECK: @"\01??0foo@@QAE@H@Z" +//X64: @"\01??0foo@@QEAA@H@Z" foo(char *q){} //CHECK: @"\01??0foo@@QAE@PAD@Z" +//X64: @"\01??0foo@@QEAA@PEAD@Z" static foo* static_method() { return 0; } @@ -77,12 +82,15 @@ enum quux { foo bar() { return foo(); } //CHECK: @"\01?bar@@YA?AVfoo@@XZ" +//X64: @"\01?bar@@YA?AVfoo@@XZ" int foo::operator+(int a) { //CHECK: @"\01??Hfoo@@QAEHH@Z" +//X64: @"\01??Hfoo@@QEAAHH@Z" foo::static_method(); //CHECK: @"\01?static_method@foo@@SAPAV1@XZ" +//X64: @"\01?static_method@foo@@SAPEAV1@XZ" bar(); return a; } @@ -109,6 +117,7 @@ int (foo2::*l)(int); static void __stdcall alpha(float a, double b) throw() {} bool __fastcall beta(long long a, wchar_t b) throw(signed char, unsigned char) { // CHECK: @"\01?beta@@YI_N_J_W@Z" +// X64: @"\01?beta@@YA_N_J_W@Z" alpha(0.f, 0.0); return false; } @@ -119,17 +128,21 @@ bool __fastcall beta(long long a, wchar_t b) throw(signed char, unsigned char) { // Make sure tag-type mangling works. void gamma(class foo, struct bar, union baz, enum quux) {} // CHECK: @"\01?gamma@@YAXVfoo@@Ubar@@Tbaz@@W4quux@@@Z" +// X64: @"\01?gamma@@YAXVfoo@@Ubar@@Tbaz@@W4quux@@@Z" // Make sure pointer/reference-type mangling works. void delta(int * const a, const long &) {} // CHECK: @"\01?delta@@YAXQAHABJ@Z" +// X64: @"\01?delta@@YAXQEAHAEBJ@Z" // Array mangling. void epsilon(int a[][10][20]) {} // CHECK: @"\01?epsilon@@YAXQAY19BE@H@Z" +// X64: @"\01?epsilon@@YAXQEAY19BE@H@Z" void zeta(int (*)(int, int)) {} // CHECK: @"\01?zeta@@YAXP6AHHH@Z@Z" +// X64: @"\01?zeta@@YAXP6AHHH@Z@Z" // Blocks mangling (Clang extension). A block should be mangled slightly // differently from a similar function pointer. @@ -158,6 +171,7 @@ void operator_new_delete() { void (redundant_parens)(); void redundant_parens_use() { redundant_parens(); } // CHECK: @"\01?redundant_parens@@YAXXZ" +// X64: @"\01?redundant_parens@@YAXXZ" // PR13047 typedef double RGB[3]; @@ -169,10 +183,12 @@ extern RGB const ((color4)[5]) = {}; // PR12603 enum E {}; // CHECK: "\01?fooE@@YA?AW4E@@XZ" +// X64: "\01?fooE@@YA?AW4E@@XZ" E fooE() { return E(); } class X {}; // CHECK: "\01?fooX@@YA?AVX@@XZ" +// X64: "\01?fooX@@YA?AVX@@XZ" X fooX() { return X(); } namespace PR13182 { -- cgit v1.2.3 From 6e01994046860c5c120c832a22557ec1c6b72ce9 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Tue, 14 May 2013 20:30:49 +0000 Subject: Fix the MSVC build broken by r181768 MSVC accepts the using decl but not the typedef. It complains that the typedef is ambiguous. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181826 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/ASTMatchers/Dynamic/VariantValue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clang/ASTMatchers/Dynamic/VariantValue.h b/include/clang/ASTMatchers/Dynamic/VariantValue.h index 8dc4238757..9a41b448cf 100644 --- a/include/clang/ASTMatchers/Dynamic/VariantValue.h +++ b/include/clang/ASTMatchers/Dynamic/VariantValue.h @@ -26,7 +26,7 @@ namespace clang { namespace ast_matchers { namespace dynamic { -typedef ast_matchers::internal::DynTypedMatcher DynTypedMatcher; +using ast_matchers::internal::DynTypedMatcher; /// \brief Variant value class. /// -- cgit v1.2.3 From d741f02da9c91c197ba8816b4b454e1ad7cd47b6 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Tue, 14 May 2013 20:39:56 +0000 Subject: Fix expression breaking for one-parameter-per-line styles. Before: if (aaaaaaaaaaaaaaaaaaaaaaaaaaaa || aaaaaaaaaaaaaaaaaaaaaaaaaaaa || aaaaaaaaaaaaaaaaaaaaaaaaaaaa || aaaaaaaaaaaaaaaaaaaaaaaaaaaa || aaaaaaaaaaaaaaaaaaaaaaaaaaaa) {} After: if (aaaaaaaaaaaaaaaaaaaaaaaaaaaa || aaaaaaaaaaaaaaaaaaaaaaaaaaaa || aaaaaaaaaaaaaaaaaaaaaaaaaaaa || aaaaaaaaaaaaaaaaaaaaaaaaaaaa || aaaaaaaaaaaaaaaaaaaaaaaaaaaa) {} git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181828 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 3 ++- unittests/Format/FormatTest.cpp | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 5cb5fef9d5..e85ecd21c2 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -543,7 +543,8 @@ private: if (State.Stack.back().AvoidBinPacking) { // If we are breaking after '(', '{', '<', this is not bin packing // unless AllowAllParametersOfDeclarationOnNextLine is false. - if ((Previous.isNot(tok::l_paren) && Previous.isNot(tok::l_brace)) || + if (!(Previous.isOneOf(tok::l_paren, tok::l_brace) || + Previous.Type == TT_BinaryOperator) || (!Style.AllowAllParametersOfDeclarationOnNextLine && Line.MustBeDeclaration)) State.Stack.back().BreakBeforeParameter = true; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 665f20322e..206343bb69 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -1646,6 +1646,14 @@ TEST_F(FormatTest, LineBreakingInBinaryExpressions) { " SourceMgr.getSpellingColumnNumber(\n" " TheLine.Last->FormatTok.Tok.getLocation()) -\n" " 1);"); + + FormatStyle OnePerLine = getLLVMStyle(); + OnePerLine.BinPackParameters = false; + verifyFormat( + "if (aaaaaaaaaaaaaaaaaaaaaaaaaaaa || aaaaaaaaaaaaaaaaaaaaaaaaaaaa ||\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaa || aaaaaaaaaaaaaaaaaaaaaaaaaaaa ||\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaa) {\n}", + OnePerLine); } TEST_F(FormatTest, ExpressionIndentation) { -- cgit v1.2.3 From a3d813a8c292bee8349ef43065dab9b344650351 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Tue, 14 May 2013 20:55:49 +0000 Subject: Fix copy-pasto in naming of LAST_MS_INHERITANCE[_ATTR] Richard Smith pointed this out over a month ago. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181830 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Attr.h | 2 +- include/clang/Basic/AttrKinds.h | 2 +- utils/TableGen/ClangAttrEmitter.cpp | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index 27dcef2a1e..e07e84a783 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -145,7 +145,7 @@ public: // Implement isa/cast/dyncast/etc. static bool classof(const Attr *A) { // Relies on relative order of enum emission with respect to param attrs. - return (A->getKind() <= attr::LAST_MS_INHERITABLE && + return (A->getKind() <= attr::LAST_MS_INHERITANCE && A->getKind() > attr::LAST_INHERITABLE_PARAM); } }; diff --git a/include/clang/Basic/AttrKinds.h b/include/clang/Basic/AttrKinds.h index bd090ecc0d..7c4e2c75f0 100644 --- a/include/clang/Basic/AttrKinds.h +++ b/include/clang/Basic/AttrKinds.h @@ -24,7 +24,7 @@ enum Kind { #define ATTR(X) X, #define LAST_INHERITABLE_ATTR(X) X, LAST_INHERITABLE = X, #define LAST_INHERITABLE_PARAM_ATTR(X) X, LAST_INHERITABLE_PARAM = X, -#define LAST_MS_INHERITABLE_ATTR(X) X, LAST_MS_INHERITABLE = X, +#define LAST_MS_INHERITANCE_ATTR(X) X, LAST_MS_INHERITANCE = X, #include "clang/Basic/AttrList.inc" NUM_ATTRS }; diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index eaf10a64a4..3982fc3f31 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -1094,13 +1094,13 @@ void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { " INHERITABLE_PARAM_ATTR(NAME)\n"; OS << "#endif\n\n"; - OS << "#ifndef MS_INHERITABLE_ATTR\n"; - OS << "#define MS_INHERITABLE_ATTR(NAME) INHERITABLE_ATTR(NAME)\n"; + OS << "#ifndef MS_INHERITANCE_ATTR\n"; + OS << "#define MS_INHERITANCE_ATTR(NAME) INHERITABLE_ATTR(NAME)\n"; OS << "#endif\n\n"; - OS << "#ifndef LAST_MS_INHERITABLE_ATTR\n"; - OS << "#define LAST_MS_INHERITABLE_ATTR(NAME)" - " MS_INHERITABLE_ATTR(NAME)\n"; + OS << "#ifndef LAST_MS_INHERITANCE_ATTR\n"; + OS << "#define LAST_MS_INHERITANCE_ATTR(NAME)" + " MS_INHERITANCE_ATTR(NAME)\n"; OS << "#endif\n\n"; Record *InhClass = Records.getClass("InheritableAttr"); @@ -1124,16 +1124,16 @@ void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { } EmitAttrList(OS, "INHERITABLE_PARAM_ATTR", InhParamAttrs); - EmitAttrList(OS, "MS_INHERITABLE_ATTR", MSInhAttrs); + EmitAttrList(OS, "MS_INHERITANCE_ATTR", MSInhAttrs); EmitAttrList(OS, "INHERITABLE_ATTR", InhAttrs); EmitAttrList(OS, "ATTR", NonInhAttrs); OS << "#undef LAST_ATTR\n"; OS << "#undef INHERITABLE_ATTR\n"; - OS << "#undef MS_INHERITABLE_ATTR\n"; + OS << "#undef MS_INHERITANCE_ATTR\n"; OS << "#undef LAST_INHERITABLE_ATTR\n"; OS << "#undef LAST_INHERITABLE_PARAM_ATTR\n"; - OS << "#undef LAST_MS_INHERITABLE_ATTR\n"; + OS << "#undef LAST_MS_INHERITANCE_ATTR\n"; OS << "#undef ATTR\n"; } -- cgit v1.2.3 From 17828ca5857d5d9cadfffd339f888de58182c8f1 Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Tue, 14 May 2013 21:04:00 +0000 Subject: Provide operator<< for stream output of DeclarationNames ASTDumper was already trying to do this & instead got an implicit bool conversion by surprise (thus printing out 0 or 1 instead of the name of the declaration). To avoid that issue & simplify call sites, simply make it the normal/expected operator<<(raw_ostream&, ...) overload & simplify all the existing call sites. (bonus: this function doesn't need to be a member or friend, it's just using public API in DeclarationName) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181832 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 2 +- include/clang/AST/DeclarationName.h | 5 +- lib/AST/ASTDiagnostic.cpp | 3 +- lib/AST/ASTDumper.cpp | 4 +- lib/AST/DeclarationName.cpp | 142 +++++++++++------------- lib/Sema/SemaLookup.cpp | 2 +- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp | 4 +- test/Misc/ast-dump-templates.cpp | 12 ++ tools/libclang/CIndexUSRs.cpp | 5 +- 9 files changed, 86 insertions(+), 93 deletions(-) diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index e35938a5e0..937035ac0a 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -142,7 +142,7 @@ public: // FIXME: Deprecated, move clients to getName(). std::string getNameAsString() const { return Name.getAsString(); } - void printName(raw_ostream &os) const { return Name.printName(os); } + void printName(raw_ostream &os) const { os << Name; } /// getDeclName - Get the actual, stored name of the declaration, /// which may be a special name. diff --git a/include/clang/AST/DeclarationName.h b/include/clang/AST/DeclarationName.h index f28882b3bf..e01b80f389 100644 --- a/include/clang/AST/DeclarationName.h +++ b/include/clang/AST/DeclarationName.h @@ -210,9 +210,6 @@ public: /// getNameAsString - Retrieve the human-readable string for this name. std::string getAsString() const; - /// printName - Print the human-readable name to a stream. - void printName(raw_ostream &OS) const; - /// getAsIdentifierInfo - Retrieve the IdentifierInfo * stored in /// this declaration name, or NULL if this declaration name isn't a /// simple identifier. @@ -302,6 +299,8 @@ public: void dump() const; }; +raw_ostream &operator<<(raw_ostream &OS, DeclarationName N); + /// Ordering on two declaration names. If both names are identifiers, /// this provides a lexicographical ordering. inline bool operator<(DeclarationName LHS, DeclarationName RHS) { diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp index 766a7a995b..e0f552ba67 100644 --- a/lib/AST/ASTDiagnostic.cpp +++ b/lib/AST/ASTDiagnostic.cpp @@ -300,8 +300,7 @@ void clang::FormatASTNodeDiagnosticArgument( assert(ModLen == 0 && ArgLen == 0 && "Invalid modifier for DeclarationName argument"); - DeclarationName N = DeclarationName::getFromOpaqueInteger(Val); - N.printName(OS); + OS << DeclarationName::getFromOpaqueInteger(Val); break; } case DiagnosticsEngine::ak_nameddecl: { diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 340cc41f7e..790538fcd0 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -449,9 +449,7 @@ void ASTDumper::dumpBareDeclRef(const Decl *D) { if (const NamedDecl *ND = dyn_cast(D)) { ColorScope Color(*this, DeclNameColor); - OS << " '"; - ND->getDeclName().printName(OS); - OS << "'"; + OS << " '" << ND->getDeclName() << '\''; } if (const ValueDecl *VD = dyn_cast(D)) diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp index e4a41b6ffb..e064e23a0a 100644 --- a/lib/AST/DeclarationName.cpp +++ b/lib/AST/DeclarationName.cpp @@ -133,6 +133,66 @@ int DeclarationName::compare(DeclarationName LHS, DeclarationName RHS) { llvm_unreachable("Invalid DeclarationName Kind!"); } +raw_ostream &operator<<(raw_ostream &OS, DeclarationName N) { + switch (N.getNameKind()) { + case DeclarationName::Identifier: + if (const IdentifierInfo *II = N.getAsIdentifierInfo()) + OS << II->getName(); + return OS; + + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + return OS << N.getObjCSelector().getAsString(); + + case DeclarationName::CXXConstructorName: { + QualType ClassType = N.getCXXNameType(); + if (const RecordType *ClassRec = ClassType->getAs()) + return OS << *ClassRec->getDecl(); + return OS << ClassType.getAsString(); + } + + case DeclarationName::CXXDestructorName: { + OS << '~'; + QualType Type = N.getCXXNameType(); + if (const RecordType *Rec = Type->getAs()) + return OS << *Rec->getDecl(); + return OS << Type.getAsString(); + } + + case DeclarationName::CXXOperatorName: { + static const char* const OperatorNames[NUM_OVERLOADED_OPERATORS] = { + 0, +#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ + Spelling, +#include "clang/Basic/OperatorKinds.def" + }; + const char *OpName = OperatorNames[N.getCXXOverloadedOperator()]; + assert(OpName && "not an overloaded operator"); + + OS << "operator"; + if (OpName[0] >= 'a' && OpName[0] <= 'z') + OS << ' '; + return OS << OpName; + } + + case DeclarationName::CXXLiteralOperatorName: + return OS << "operator \"\" " << N.getCXXLiteralIdentifier()->getName(); + + case DeclarationName::CXXConversionFunctionName: { + OS << "operator "; + QualType Type = N.getCXXNameType(); + if (const RecordType *Rec = Type->getAs()) + return OS << *Rec->getDecl(); + return OS << Type.getAsString(); + } + case DeclarationName::CXXUsingDirective: + return OS << ""; + } + + llvm_unreachable("Unexpected declaration name kind"); +} + } // end namespace clang DeclarationName::NameKind DeclarationName::getNameKind() const { @@ -180,80 +240,10 @@ bool DeclarationName::isDependentName() const { std::string DeclarationName::getAsString() const { std::string Result; llvm::raw_string_ostream OS(Result); - printName(OS); + OS << *this; return OS.str(); } -void DeclarationName::printName(raw_ostream &OS) const { - switch (getNameKind()) { - case Identifier: - if (const IdentifierInfo *II = getAsIdentifierInfo()) - OS << II->getName(); - return; - - case ObjCZeroArgSelector: - case ObjCOneArgSelector: - case ObjCMultiArgSelector: - OS << getObjCSelector().getAsString(); - return; - - case CXXConstructorName: { - QualType ClassType = getCXXNameType(); - if (const RecordType *ClassRec = ClassType->getAs()) - OS << *ClassRec->getDecl(); - else - OS << ClassType.getAsString(); - return; - } - - case CXXDestructorName: { - OS << '~'; - QualType Type = getCXXNameType(); - if (const RecordType *Rec = Type->getAs()) - OS << *Rec->getDecl(); - else - OS << Type.getAsString(); - return; - } - - case CXXOperatorName: { - static const char* const OperatorNames[NUM_OVERLOADED_OPERATORS] = { - 0, -#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ - Spelling, -#include "clang/Basic/OperatorKinds.def" - }; - const char *OpName = OperatorNames[getCXXOverloadedOperator()]; - assert(OpName && "not an overloaded operator"); - - OS << "operator"; - if (OpName[0] >= 'a' && OpName[0] <= 'z') - OS << ' '; - OS << OpName; - return; - } - - case CXXLiteralOperatorName: - OS << "operator \"\" " << getCXXLiteralIdentifier()->getName(); - return; - - case CXXConversionFunctionName: { - OS << "operator "; - QualType Type = getCXXNameType(); - if (const RecordType *Rec = Type->getAs()) - OS << *Rec->getDecl(); - else - OS << Type.getAsString(); - return; - } - case CXXUsingDirective: - OS << ""; - return; - } - - llvm_unreachable("Unexpected declaration name kind"); -} - QualType DeclarationName::getCXXNameType() const { if (CXXSpecialName *CXXName = getAsCXXSpecialName()) return CXXName->Type; @@ -336,8 +326,7 @@ DeclarationName DeclarationName::getUsingDirectiveName() { } void DeclarationName::dump() const { - printName(llvm::errs()); - llvm::errs() << '\n'; + llvm::errs() << *this << '\n'; } DeclarationNameTable::DeclarationNameTable(const ASTContext &C) : Ctx(C) { @@ -537,7 +526,7 @@ void DeclarationNameInfo::printName(raw_ostream &OS) const { case DeclarationName::CXXOperatorName: case DeclarationName::CXXLiteralOperatorName: case DeclarationName::CXXUsingDirective: - Name.printName(OS); + OS << Name; return; case DeclarationName::CXXConstructorName: @@ -549,9 +538,8 @@ void DeclarationNameInfo::printName(raw_ostream &OS) const { else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName) OS << "operator "; OS << TInfo->getType().getAsString(); - } - else - Name.printName(OS); + } else + OS << Name; return; } llvm_unreachable("Unexpected declaration name kind"); diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 9ab3b2d08e..c70367691f 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -4166,7 +4166,7 @@ std::string TypoCorrection::getAsString(const LangOptions &LO) const { std::string tmpBuffer; llvm::raw_string_ostream PrefixOStream(tmpBuffer); CorrectionNameSpec->print(PrefixOStream, PrintingPolicy(LO)); - CorrectionName.printName(PrefixOStream); + PrefixOStream << CorrectionName; return PrefixOStream.str(); } diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 00c92d8153..fe579a3fbf 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1465,9 +1465,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, SmallString<256> Buf; llvm::raw_svector_ostream Out(Buf); - Out << "Assuming '"; - VD->getDeclName().printName(Out); - Out << "' is "; + Out << "Assuming '" << VD->getDeclName() << "' is "; QualType VDTy = VD->getType(); diff --git a/test/Misc/ast-dump-templates.cpp b/test/Misc/ast-dump-templates.cpp index 7e28da95a1..b7aeca8d55 100644 --- a/test/Misc/ast-dump-templates.cpp +++ b/test/Misc/ast-dump-templates.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -ast-print %s > %t // RUN: FileCheck < %t %s -check-prefix=CHECK1 // RUN: FileCheck < %t %s -check-prefix=CHECK2 +// RUN: %clang_cc1 -ast-dump %s | FileCheck --check-prefix=DUMP %s template struct foo { @@ -37,3 +38,14 @@ void baz() { // Template definition - bar // CHECK1: template B bar() // CHECK2: template B bar() + +namespace test2 { +void func(int); +void func(float); +template +void tmpl() { + func(T()); +} + +// DUMP: UnresolvedLookupExpr {{.*}} '' lvalue (ADL) = 'func' +} diff --git a/tools/libclang/CIndexUSRs.cpp b/tools/libclang/CIndexUSRs.cpp index 96c1d2e11d..4e47435996 100644 --- a/tools/libclang/CIndexUSRs.cpp +++ b/tools/libclang/CIndexUSRs.cpp @@ -309,9 +309,8 @@ void USRGenerator::VisitObjCMethodDecl(const ObjCMethodDecl *D) { // Ideally we would use 'GenObjCMethod', but this is such a hot path // for Objective-C code that we don't want to use // DeclarationName::getAsString(). - Out << (D->isInstanceMethod() ? "(im)" : "(cm)"); - DeclarationName N(D->getSelector()); - N.printName(Out); + Out << (D->isInstanceMethod() ? "(im)" : "(cm)") + << DeclarationName(D->getSelector()); } void USRGenerator::VisitObjCContainerDecl(const ObjCContainerDecl *D) { -- cgit v1.2.3 From e7e94c907cf6924929e81083e93628d0f520c734 Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Tue, 14 May 2013 21:31:46 +0000 Subject: Fix clang -Werror build due to -Wreorder violation introduced in r181825 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181834 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/MicrosoftMangle.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index dbd37f0201..d03060b391 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -69,17 +69,17 @@ public: MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_) : Context(C), Out(Out_), Structor(0), StructorType(-1), + UseNameBackReferences(true), PointersAre64Bit(C.getASTContext().getTargetInfo().getPointerWidth(0) == - 64), - UseNameBackReferences(true) { } + 64) { } MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_, const CXXDestructorDecl *D, CXXDtorType Type) : Context(C), Out(Out_), Structor(getStructor(D)), StructorType(Type), + UseNameBackReferences(true), PointersAre64Bit(C.getASTContext().getTargetInfo().getPointerWidth(0) == - 64), - UseNameBackReferences(true) { } + 64) { } raw_ostream &getStream() const { return Out; } -- cgit v1.2.3 From 910f17e331221cd0833d0b5b49013cbbc7ef122a Mon Sep 17 00:00:00 2001 From: Richard Trieu Date: Tue, 14 May 2013 21:59:17 +0000 Subject: When computing the size of large arrays, use char units instead of bits. This prevents an overflow and assertion when the number of bits cannot be stored in 64-bits. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181839 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ASTContext.cpp | 23 +++++++++++++++++++++-- test/Sema/offsetof.c | 12 ++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 21a16a6d98..c385d6cf3a 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1345,8 +1345,27 @@ ASTContext::getTypeInfoDataSizeInChars(QualType T) const { return sizeAndAlign; } +/// getConstantArrayInfoInChars - Performing the computation in CharUnits +/// instead of in bits prevents overflowing the uint64_t for some large arrays. +std::pair +static getConstantArrayInfoInChars(const ASTContext &Context, + const ConstantArrayType *CAT) { + std::pair EltInfo = + Context.getTypeInfoInChars(CAT->getElementType()); + uint64_t Size = CAT->getSize().getZExtValue(); + assert((Size == 0 || EltInfo.first.getQuantity() <= (uint64_t)(-1)/Size) && + "Overflow in array type char size evaluation"); + uint64_t Width = EltInfo.first.getQuantity() * Size; + unsigned Align = EltInfo.second.getQuantity(); + Width = llvm::RoundUpToAlignment(Width, Align); + return std::make_pair(CharUnits::fromQuantity(Width), + CharUnits::fromQuantity(Align)); +} + std::pair ASTContext::getTypeInfoInChars(const Type *T) const { + if (const ConstantArrayType *CAT = dyn_cast(T)) + return getConstantArrayInfoInChars(*this, CAT); std::pair Info = getTypeInfo(T); return std::make_pair(toCharUnitsFromBits(Info.first), toCharUnitsFromBits(Info.second)); @@ -1702,10 +1721,10 @@ int64_t ASTContext::toBits(CharUnits CharSize) const { /// getTypeSizeInChars - Return the size of the specified type, in characters. /// This method does not work on incomplete types. CharUnits ASTContext::getTypeSizeInChars(QualType T) const { - return toCharUnitsFromBits(getTypeSize(T)); + return getTypeInfoInChars(T).first; } CharUnits ASTContext::getTypeSizeInChars(const Type *T) const { - return toCharUnitsFromBits(getTypeSize(T)); + return getTypeInfoInChars(T).first; } /// getTypeAlignInChars - Return the ABI-specified alignment of a type, in diff --git a/test/Sema/offsetof.c b/test/Sema/offsetof.c index 46fb515c7f..4e7fd7f29e 100644 --- a/test/Sema/offsetof.c +++ b/test/Sema/offsetof.c @@ -69,3 +69,15 @@ int test4 = __builtin_offsetof(Array, array); int test5() { return __builtin_offsetof(Array, array[*(int*)0]); // expected-warning{{indirection of non-volatile null pointer}} expected-note{{__builtin_trap}} } + +// PR15216 +// Don't crash when taking computing the offset of structs with large arrays. +const unsigned long Size = (1l << 62); + +struct Chunk { + char padding[Size]; + char more_padding[1][Size]; + char data; +}; + +int test6 = __builtin_offsetof(struct Chunk, data); -- cgit v1.2.3 From 86a87308f11e7ee7c71ff608aec0cde11aa7df58 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 14 May 2013 23:22:32 +0000 Subject: Don't mark a type specifier as "owned" if there is no declaration to own. This simplifies error recovery elsewhere, eliminating the crash in . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181846 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/DeclSpec.cpp | 2 +- test/SemaCXX/nested-name-spec.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index 3b3ab2c27b..dfce324b70 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -651,7 +651,7 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc, DeclRep = Rep; TSTLoc = TagKwLoc; TSTNameLoc = TagNameLoc; - TypeSpecOwned = Owned; + TypeSpecOwned = Owned && Rep != 0; return false; } diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp index 7239646d8d..228bc0ecbd 100644 --- a/test/SemaCXX/nested-name-spec.cpp +++ b/test/SemaCXX/nested-name-spec.cpp @@ -297,3 +297,13 @@ namespace NS { int foobar = a + longer_b; // expected-error {{use of undeclared identifier 'a'; did you mean 'NS::a'?}} \ // expected-error {{use of undeclared identifier 'longer_b'; did you mean 'NS::longer_b'?}} } + +// +namespace N { + struct X { }; + namespace N { + struct Foo { + struct N::X *foo(); // expected-error{{no struct named 'X' in namespace 'N::N'}} + }; + } +} -- cgit v1.2.3 From 9f00b1d3962147a2fe049b8b45f70680bc12fbc1 Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Tue, 14 May 2013 23:24:17 +0000 Subject: Objective-C [diagnostics] [QOI], when method is not found for a receiver, note where receiver class is declaraed (this is most common when receiver is a forward class). // rdar://3258331 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181847 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 2 ++ lib/Sema/SemaExprObjC.cpp | 11 ++++++++++- test/Analysis/inlining/DynDispatchBifurcate.m | 4 ++-- test/Analysis/rdar-6540084.m | 4 ++-- test/Modules/objc-categories.m | 1 + test/PCH/chain-categories2.m | 1 + test/SemaObjC/call-super-2.m | 2 +- test/SemaObjC/compare-qualified-id.m | 2 +- test/SemaObjC/conditional-expr.m | 3 ++- test/SemaObjC/error-outof-scope-property-use.m | 2 +- test/SemaObjC/instancetype.m | 2 +- test/SemaObjC/message.m | 2 +- test/SemaObjC/method-not-defined.m | 2 +- test/SemaObjC/missing-atend-metadata.m | 2 +- test/SemaObjC/property-5.m | 2 +- test/SemaObjC/protocol-id-test-1.m | 2 +- test/SemaObjCXX/instancetype.mm | 2 +- 17 files changed, 30 insertions(+), 16 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c2728820d4..db4696767a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -499,6 +499,8 @@ def note_while_in_implementation : Note< "detected while default synthesizing properties in class implementation">; def note_class_declared : Note< "class is declared here">; +def note_receiver_class_declared : Note< + "receiver is object of the class that is declared here">; def note_receiver_is_id : Note< "receiver is treated with 'id' type for purpose of method lookup">; def note_suppressed_class_declare : Note< diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index d37f61ab8a..7c701ea040 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -1221,10 +1221,19 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, else DiagID = isClassMessage ? diag::warn_class_method_not_found : diag::warn_inst_method_not_found; - if (!getLangOpts().DebuggerSupport) + if (!getLangOpts().DebuggerSupport) { Diag(SelLoc, DiagID) << Sel << isClassMessage << SourceRange(SelectorLocs.front(), SelectorLocs.back()); + // Find the class to which we are sending this message. + if (ReceiverType->isObjCObjectPointerType()) { + QualType ClassType = + ReceiverType->getAs()->getPointeeType(); + if (const ObjCObjectType *ClassTPtr = ClassType->getAs()) + if (ObjCInterfaceDecl *Class = ClassTPtr->getInterface()) + Diag(Class->getLocation(), diag::note_receiver_class_declared); + } + } // In debuggers, we want to use __unknown_anytype for these // results so that clients can cast them. diff --git a/test/Analysis/inlining/DynDispatchBifurcate.m b/test/Analysis/inlining/DynDispatchBifurcate.m index ab1dfc5ec1..d8af213a06 100644 --- a/test/Analysis/inlining/DynDispatchBifurcate.m +++ b/test/Analysis/inlining/DynDispatchBifurcate.m @@ -181,11 +181,11 @@ int testPropertySynthesized(PublicClass *p) { } // Test definition not available edge case. -@interface DefNotAvailClass : NSObject +@interface DefNotAvailClass : NSObject // expected-note {{receiver is object of the class that is declared here}} @end id testDefNotAvailableInlined(DefNotAvailClass *C) { return [C mem]; // expected-warning {{instance method '-mem' not found}} } id testDefNotAvailable(DefNotAvailClass *C) { return testDefNotAvailableInlined(C); -} \ No newline at end of file +} diff --git a/test/Analysis/rdar-6540084.m b/test/Analysis/rdar-6540084.m index 7070709e57..d93450b5c5 100644 --- a/test/Analysis/rdar-6540084.m +++ b/test/Analysis/rdar-6540084.m @@ -9,13 +9,13 @@ typedef struct _NSZone NSZone; @protocol NSObject - (BOOL)isEqual:(id)object; @end @interface NSObject {} @end extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone); -@class NSArray; +@class NSArray; // expected-note {{receiver is object of the class that is declared here}} @class NSMutableArray, NSIndexSet, NSView, NSPredicate, NSString, NSViewAnimation, NSTimer; // expected-note{{forward declaration of class here}} @interface FooBazController : NSObject {} @end typedef struct {} TazVersion; @class TazNode; -@interface TazGuttenberg : NSObject {} typedef NSUInteger BugsBunnyType; @end +@interface TazGuttenberg : NSObject {} typedef NSUInteger BugsBunnyType; @end // expected-note {{receiver is object of the class that is declared here}} @interface FooBaz : NSObject {} @property (nonatomic) BugsBunnyType matchType; @property (nonatomic, retain) NSArray *papyrus; @end diff --git a/test/Modules/objc-categories.m b/test/Modules/objc-categories.m index 81fb28bafb..07d1760980 100644 --- a/test/Modules/objc-categories.m +++ b/test/Modules/objc-categories.m @@ -10,6 +10,7 @@ // expected-note@Inputs/category_left.h:14 {{previous definition}} // expected-warning@Inputs/category_right.h:11 {{duplicate definition of category}} +// expected-note@Inputs/category_top.h:1 {{receiver is object of the class that is declared here}} @interface Foo(Source) -(void)source; diff --git a/test/PCH/chain-categories2.m b/test/PCH/chain-categories2.m index f230bf9348..8cb06a857c 100644 --- a/test/PCH/chain-categories2.m +++ b/test/PCH/chain-categories2.m @@ -45,6 +45,7 @@ #else //===----------------------------------------------------------------------===// +// expected-note@30 {{receiver is object of the class that is declared here}} void f(I* i) { [i meth]; // expected-warning {{not found}} } diff --git a/test/SemaObjC/call-super-2.m b/test/SemaObjC/call-super-2.m index 3c45a2c732..18a5f09407 100644 --- a/test/SemaObjC/call-super-2.m +++ b/test/SemaObjC/call-super-2.m @@ -14,7 +14,7 @@ id objc_getClass(const char *s); - (int) instance_func0; @end -@interface Derived: Object +@interface Derived: Object // expected-note {{receiver is object of the class that is declared here}} + (int) class_func1; + (int) class_func2; + (int) class_func3; diff --git a/test/SemaObjC/compare-qualified-id.m b/test/SemaObjC/compare-qualified-id.m index 82868f8a16..0b7e106b35 100644 --- a/test/SemaObjC/compare-qualified-id.m +++ b/test/SemaObjC/compare-qualified-id.m @@ -12,7 +12,7 @@ typedef struct _NSZone NSZone; typedef struct {} NSFastEnumerationState; @protocol NSFastEnumeration - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len; @end @interface NSDictionary : NSObject - (NSUInteger)count; @end -@interface NSMutableDictionary : NSDictionary - (void)removeObjectForKey:(id)aKey; @end +@interface NSMutableDictionary : NSDictionary - (void)removeObjectForKey:(id)aKey; @end // expected-note {{receiver is object of the class that is declared here}} extern NSString * const NSTaskDidTerminateNotification; @interface XCPropertyExpansionContext : NSObject { // expected-note {{required for direct or indirect protocol 'NSCopying'}} diff --git a/test/SemaObjC/conditional-expr.m b/test/SemaObjC/conditional-expr.m index ec1305dbe8..083453810c 100644 --- a/test/SemaObjC/conditional-expr.m +++ b/test/SemaObjC/conditional-expr.m @@ -33,7 +33,8 @@ @end // No @interface declaration for DTFilterOutputStream3 -@implementation DTFilterOutputStream3 // expected-warning {{cannot find interface declaration for 'DTFilterOutputStream3'}} +@implementation DTFilterOutputStream3 // expected-warning {{cannot find interface declaration for 'DTFilterOutputStream3'}} \ + // expected-note {{receiver is object of the class that is declared here}} - (id)initWithNextOutputStream:(id ) outputStream { id nextOutputStream = [self nextOutputStream]; // expected-warning {{method '-nextOutputStream' not found (return type defaults to 'id')}} self = nextOutputStream; // expected-warning {{assigning to 'DTFilterOutputStream3 *' from incompatible type 'id'}} diff --git a/test/SemaObjC/error-outof-scope-property-use.m b/test/SemaObjC/error-outof-scope-property-use.m index c69a4055df..023687bbf4 100644 --- a/test/SemaObjC/error-outof-scope-property-use.m +++ b/test/SemaObjC/error-outof-scope-property-use.m @@ -2,7 +2,7 @@ // RUN: %clang_cc1 -x objective-c++ -fsyntax-only -fobjc-default-synthesize-properties -verify -Wno-objc-root-class %s // rdar://13178483 -@class NSMutableDictionary; +@class NSMutableDictionary; // expected-note {{receiver is object of the class that is declared here}} @interface LaunchdJobs diff --git a/test/SemaObjC/instancetype.m b/test/SemaObjC/instancetype.m index 8137964737..2374890271 100644 --- a/test/SemaObjC/instancetype.m +++ b/test/SemaObjC/instancetype.m @@ -25,7 +25,7 @@ - (instancetype)otherMethodInProto2; // expected-note{{overridden method returns an instance of its class type}} @end -@interface Subclass1 : Root +@interface Subclass1 : Root // expected-note 4 {{receiver is object of the class that is declared here}} - (instancetype)initSubclass1; - (void)methodOnSubclass1; + (instancetype)allocSubclass1; diff --git a/test/SemaObjC/message.m b/test/SemaObjC/message.m index 40fa102f35..fc75cdbf6e 100644 --- a/test/SemaObjC/message.m +++ b/test/SemaObjC/message.m @@ -107,7 +107,7 @@ void foo5(id p) { // expected-warning {{instance method '-bar' not found}} } -@interface I1 +@interface I1 // expected-note {{receiver is object of the class that is declared here}} -(void)unavail_meth __attribute__((unavailable)); // expected-note {{marked unavailable here}} @end diff --git a/test/SemaObjC/method-not-defined.m b/test/SemaObjC/method-not-defined.m index 22466f7dc3..8f43c5daa7 100644 --- a/test/SemaObjC/method-not-defined.m +++ b/test/SemaObjC/method-not-defined.m @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -@interface Foo +@interface Foo // expected-note {{receiver is object of the class that is declared here}} @end void test() { diff --git a/test/SemaObjC/missing-atend-metadata.m b/test/SemaObjC/missing-atend-metadata.m index f072981dc1..9d0024ac65 100644 --- a/test/SemaObjC/missing-atend-metadata.m +++ b/test/SemaObjC/missing-atend-metadata.m @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -triple i386-apple-darwin9 -fsyntax-only -verify -Wno-objc-root-class %s -@interface I0 +@interface I0 // expected-note {{receiver is object of the class that is declared here}} @end @implementation I0 // expected-note {{implementation started here}} diff --git a/test/SemaObjC/property-5.m b/test/SemaObjC/property-5.m index cd7cc2487a..782f77cfea 100644 --- a/test/SemaObjC/property-5.m +++ b/test/SemaObjC/property-5.m @@ -8,7 +8,7 @@ @interface MutableNSData : NSData @end -@interface Base : NSData +@interface Base : NSData // expected-note {{receiver is object of the class that is declared here}} @property(readonly) id ref; @property(readonly) Base *p_base; @property(readonly) NSData *nsdata; diff --git a/test/SemaObjC/protocol-id-test-1.m b/test/SemaObjC/protocol-id-test-1.m index 19a4432de6..013f291ad9 100644 --- a/test/SemaObjC/protocol-id-test-1.m +++ b/test/SemaObjC/protocol-id-test-1.m @@ -7,7 +7,7 @@ @protocol P @end -@interface INTF

+@interface INTF

// expected-note {{receiver is object of the class that is declared here}} - (void)IMeth; @end diff --git a/test/SemaObjCXX/instancetype.mm b/test/SemaObjCXX/instancetype.mm index bbf100ef04..87663b9716 100644 --- a/test/SemaObjCXX/instancetype.mm +++ b/test/SemaObjCXX/instancetype.mm @@ -25,7 +25,7 @@ - (instancetype)otherMethodInProto2; // expected-note{{overridden method returns an instance of its class type}} @end -@interface Subclass1 : Root +@interface Subclass1 : Root // expected-note 4 {{receiver is object of the class that is declared here}} - (instancetype)initSubclass1; - (void)methodOnSubclass1; + (instancetype)allocSubclass1; -- cgit v1.2.3 From 1069b731a0dc872069c2d6572eefe424b0cceff3 Mon Sep 17 00:00:00 2001 From: Richard Trieu Date: Tue, 14 May 2013 23:41:50 +0000 Subject: Add static_cast to assertion to silence sign/unsigned comparison warning. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181849 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ASTContext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index c385d6cf3a..b19150f048 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1353,7 +1353,8 @@ static getConstantArrayInfoInChars(const ASTContext &Context, std::pair EltInfo = Context.getTypeInfoInChars(CAT->getElementType()); uint64_t Size = CAT->getSize().getZExtValue(); - assert((Size == 0 || EltInfo.first.getQuantity() <= (uint64_t)(-1)/Size) && + assert((Size == 0 || static_cast(EltInfo.first.getQuantity()) <= + (uint64_t)(-1)/Size) && "Overflow in array type char size evaluation"); uint64_t Width = EltInfo.first.getQuantity() * Size; unsigned Align = EltInfo.second.getQuantity(); -- cgit v1.2.3 From 2a3c9664b826963a294b0e07141baad9517d402b Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Tue, 14 May 2013 23:52:21 +0000 Subject: Unbreaking the MSVC build by adding an include. It broke with r181832. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181853 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Decl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 937035ac0a..aafd0bc530 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -24,6 +24,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" namespace clang { struct ASTTemplateArgumentListInfo; -- cgit v1.2.3 From 1bb8cb5558f46502b84e3b2935b7f5b4c1759d21 Mon Sep 17 00:00:00 2001 From: Richard Trieu Date: Wed, 15 May 2013 00:44:06 +0000 Subject: Move a test that requires 64-bit mode to a separate test with a triple in the run line. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181854 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Sema/offsetof-64.c | 15 +++++++++++++++ test/Sema/offsetof.c | 11 ----------- 2 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 test/Sema/offsetof-64.c diff --git a/test/Sema/offsetof-64.c b/test/Sema/offsetof-64.c new file mode 100644 index 0000000000..1cabec9842 --- /dev/null +++ b/test/Sema/offsetof-64.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-linux-gnu +// expected-no-diagnostics + +// PR15216 +// Don't crash when taking computing the offset of structs with large arrays. +const unsigned long Size = (1l << 62); + +struct Chunk { + char padding[Size]; + char more_padding[1][Size]; + char data; +}; + +int test1 = __builtin_offsetof(struct Chunk, data); + diff --git a/test/Sema/offsetof.c b/test/Sema/offsetof.c index 4e7fd7f29e..9e876ad589 100644 --- a/test/Sema/offsetof.c +++ b/test/Sema/offsetof.c @@ -70,14 +70,3 @@ int test5() { return __builtin_offsetof(Array, array[*(int*)0]); // expected-warning{{indirection of non-volatile null pointer}} expected-note{{__builtin_trap}} } -// PR15216 -// Don't crash when taking computing the offset of structs with large arrays. -const unsigned long Size = (1l << 62); - -struct Chunk { - char padding[Size]; - char more_padding[1][Size]; - char data; -}; - -int test6 = __builtin_offsetof(struct Chunk, data); -- cgit v1.2.3 From 8a793dcbc1290ee00fd6d31b89e67b770912d8cc Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Wed, 15 May 2013 01:45:37 +0000 Subject: Use 'env' in tests that set environment variables. Patch by David Fang! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181861 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Tooling/auto-detect-from-source-parent-of-cwd.cpp | 2 +- test/Tooling/clang-check-pwd.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp b/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp index 6b632b0a0d..b1778a65e7 100644 --- a/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp +++ b/test/Tooling/auto-detect-from-source-parent-of-cwd.cpp @@ -2,7 +2,7 @@ // RUN: mkdir -p %t/abc/def/ijk/qwe // RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/abc/def/ijk/qwe/test.cpp\",\"file\":\"%t/abc/def/ijk/qwe/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json // RUN: cp "%s" "%t/abc/def/ijk/qwe/test.cpp" -// RUN: PWD="%t/abc/def" clang-check "ijk/qwe/test.cpp" 2>&1 | FileCheck %s +// RUN: env PWD="%t/abc/def" clang-check "ijk/qwe/test.cpp" 2>&1 | FileCheck %s // CHECK: C++ requires invalid; diff --git a/test/Tooling/clang-check-pwd.cpp b/test/Tooling/clang-check-pwd.cpp index 463ed40b3e..ac245f7ec5 100644 --- a/test/Tooling/clang-check-pwd.cpp +++ b/test/Tooling/clang-check-pwd.cpp @@ -2,7 +2,7 @@ // RUN: mkdir %t // RUN: echo "[{\"directory\":\".\",\"command\":\"clang++ -c %t/test.cpp\",\"file\":\"%t/test.cpp\"}]" | sed -e 's/\\/\\\\/g' > %t/compile_commands.json // RUN: cp "%s" "%t/test.cpp" -// RUN: PWD="%t" clang-check -p "%t" "test.cpp" 2>&1|FileCheck %s +// RUN: env PWD="%t" clang-check -p "%t" "test.cpp" 2>&1|FileCheck %s // FIXME: Make the above easier. // CHECK: C++ requires -- cgit v1.2.3 From cd76539274cdc3907a61de28ba81a9e90f270a41 Mon Sep 17 00:00:00 2001 From: Jim Grosbach Date: Wed, 15 May 2013 02:40:04 +0000 Subject: ARM: Improve codegen for vget_low_* and vget_high_ intrinsics. These intrinsics use the __builtin_shuffle() function to extract the low and high half, respectively, of a 128-bit NEON vector. Currently, they're defined to use bitcasts to simplify the emitter, so we get code like: uint16x4_t vget_low_u32(uint16x8_t __a) { return (uint32x2_t) __builtin_shufflevector((int64x2_t) __a, (int64x2_t) __a, 0); } While this works, it results in those bitcasts going all the way through to the IR, resulting in code like: %1 = bitcast <8 x i16> %in to <2 x i64> %2 = shufflevector <2 x i64> %1, <2 x i64> undef, <1 x i32> %zeroinitializer %3 = bitcast <1 x i64> %2 to <4 x i16> We can instead easily perform the operation directly on the input vector like: uint16x4_t vget_low_u16(uint16x8_t __a) { return __builtin_shufflevector(__a, __a, 0, 1, 2, 3); } Not only is that much easier to read on its own, it also results in cleaner IR like: %1 = shufflevector <8 x i16> %in, <8 x i16> undef, <4 x i32> This is both easier to read and easier for the back end to reason about effectively since the operation is obfuscating the source with bitcasts. rdar://13894163 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181865 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/CodeGen/arm-neon-vget.c | 124 +++++++++++++++++++++++++++++++++++++++++ utils/TableGen/NeonEmitter.cpp | 13 +++-- 2 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 test/CodeGen/arm-neon-vget.c diff --git a/test/CodeGen/arm-neon-vget.c b/test/CodeGen/arm-neon-vget.c new file mode 100644 index 0000000000..4a710a2ad8 --- /dev/null +++ b/test/CodeGen/arm-neon-vget.c @@ -0,0 +1,124 @@ +// REQUIRES: arm-registered-target +// RUN: %clang_cc1 -triple thumbv7-apple-darwin \ +// RUN: -target-abi apcs-gnu \ +// RUN: -target-cpu cortex-a8 \ +// RUN: -mfloat-abi soft \ +// RUN: -target-feature +soft-float-abi \ +// RUN: -ffreestanding \ +// RUN: -emit-llvm -w -O1 -o - %s | FileCheck %s + +#include + +// Check that the vget_low/vget_high intrinsics generate a single shuffle +// without any bitcasting. +int8x8_t low_s8(int8x16_t a) { +// CHECK: shufflevector <16 x i8> %a, <16 x i8> undef, <8 x i32> + return vget_low_s8(a); +} + +uint8x8_t low_u8 (uint8x16_t a) { +// CHECK: shufflevector <16 x i8> %a, <16 x i8> undef, <8 x i32> + return vget_low_u8(a); +} + +int16x4_t low_s16( int16x8_t a) { +// CHECK: shufflevector <8 x i16> %a, <8 x i16> undef, <4 x i32> + return vget_low_s16(a); +} + +uint16x4_t low_u16(uint16x8_t a) { +// CHECK: shufflevector <8 x i16> %a, <8 x i16> undef, <4 x i32> + return vget_low_u16(a); +} + +int32x2_t low_s32( int32x4_t a) { +// CHECK: shufflevector <4 x i32> %a, <4 x i32> undef, <2 x i32> + return vget_low_s32(a); +} + +uint32x2_t low_u32(uint32x4_t a) { +// CHECK: shufflevector <4 x i32> %a, <4 x i32> undef, <2 x i32> + return vget_low_u32(a); +} + +int64x1_t low_s64( int64x2_t a) { +// CHECK: shufflevector <2 x i64> %a, <2 x i64> undef, <1 x i32> zeroinitializer + return vget_low_s64(a); +} + +uint64x1_t low_u64(uint64x2_t a) { +// CHECK: shufflevector <2 x i64> %a, <2 x i64> undef, <1 x i32> zeroinitializer + return vget_low_u64(a); +} + +poly8x8_t low_p8 (poly8x16_t a) { +// CHECK: shufflevector <16 x i8> %a, <16 x i8> undef, <8 x i32> + return vget_low_p8(a); +} + +poly16x4_t low_p16(poly16x8_t a) { +// CHECK: shufflevector <8 x i16> %a, <8 x i16> undef, <4 x i32> + return vget_low_p16(a); +} + +float32x2_t low_f32(float32x4_t a) { +// CHECK: shufflevector <4 x float> %a, <4 x float> undef, <2 x i32> + return vget_low_f32(a); +} + + +int8x8_t high_s8(int8x16_t a) { +// CHECK: shufflevector <16 x i8> %a, <16 x i8> undef, <8 x i32> + return vget_high_s8(a); +} + +uint8x8_t high_u8 (uint8x16_t a) { +// CHECK: shufflevector <16 x i8> %a, <16 x i8> undef, <8 x i32> + return vget_high_u8(a); +} + +int16x4_t high_s16( int16x8_t a) { +// CHECK: shufflevector <8 x i16> %a, <8 x i16> undef, <4 x i32> + return vget_high_s16(a); +} + +uint16x4_t high_u16(uint16x8_t a) { +// CHECK: shufflevector <8 x i16> %a, <8 x i16> undef, <4 x i32> + return vget_high_u16(a); +} + +int32x2_t high_s32( int32x4_t a) { +// CHECK: shufflevector <4 x i32> %a, <4 x i32> undef, <2 x i32> + return vget_high_s32(a); +} + +uint32x2_t high_u32(uint32x4_t a) { +// CHECK: shufflevector <4 x i32> %a, <4 x i32> undef, <2 x i32> + return vget_high_u32(a); +} + +int64x1_t high_s64( int64x2_t a) { +// CHECK: shufflevector <2 x i64> %a, <2 x i64> undef, <1 x i32> + return vget_high_s64(a); +} + +uint64x1_t high_u64(uint64x2_t a) { +// CHECK: shufflevector <2 x i64> %a, <2 x i64> undef, <1 x i32> + return vget_high_u64(a); +} + +poly8x8_t high_p8 (poly8x16_t a) { +// CHECK: shufflevector <16 x i8> %a, <16 x i8> undef, <8 x i32> + return vget_high_p8(a); +} + +poly16x4_t high_p16(poly16x8_t a) { +// CHECK: shufflevector <8 x i16> %a, <8 x i16> undef, <4 x i32> + return vget_high_p16(a); +} + +float32x2_t high_f32(float32x4_t a) { +// CHECK: shufflevector <4 x float> %a, <4 x float> undef, <2 x i32> + return vget_high_f32(a); +} + diff --git a/utils/TableGen/NeonEmitter.cpp b/utils/TableGen/NeonEmitter.cpp index 34b955e8e9..05505c99c9 100644 --- a/utils/TableGen/NeonEmitter.cpp +++ b/utils/TableGen/NeonEmitter.cpp @@ -1410,12 +1410,17 @@ static std::string GenOpString(OpKind op, const std::string &proto, s += ", (int64x1_t)__b, 0, 1);"; break; case OpHi: - s += "(" + ts + - ")__builtin_shufflevector((int64x2_t)__a, (int64x2_t)__a, 1);"; + // nElts is for the result vector, so the source is twice that number. + s += "__builtin_shufflevector(__a, __a"; + for (unsigned i = nElts; i < nElts * 2; ++i) + s += ", " + utostr(i); + s+= ");"; break; case OpLo: - s += "(" + ts + - ")__builtin_shufflevector((int64x2_t)__a, (int64x2_t)__a, 0);"; + s += "__builtin_shufflevector(__a, __a"; + for (unsigned i = 0; i < nElts; ++i) + s += ", " + utostr(i); + s+= ");"; break; case OpDup: s += Duplicate(nElts, typestr, "__a") + ";"; -- cgit v1.2.3 From fcec0c991edbb011a1eeb85d8de836502f799aed Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Wed, 15 May 2013 03:22:33 +0000 Subject: Use correct types for SPARC v9. It's an LP64 platform. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181867 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Basic/Targets.cpp | 5 +++++ test/Preprocessor/init.c | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index a622a11aa5..6b376b30ad 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -4304,6 +4304,11 @@ public: // FIXME: Support Sparc quad-precision long double? DescriptionString = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" "i64:64:64-f32:32:32-f64:64:64-v64:64:64-n32:64-S128"; + // This is an LP64 platform. + LongWidth = LongAlign = PointerWidth = PointerAlign = 64; + IntMaxType = SignedLong; + UIntMaxType = UnsignedLong; + Int64Type = SignedLong; } virtual void getTargetDefines(const LangOptions &Opts, diff --git a/test/Preprocessor/init.c b/test/Preprocessor/init.c index 9671f7e232..b3af46e575 100644 --- a/test/Preprocessor/init.c +++ b/test/Preprocessor/init.c @@ -2688,6 +2688,14 @@ // X86_64-LINUX:#define __x86_64 1 // X86_64-LINUX:#define __x86_64__ 1 // +// RUN: %clang_cc1 -E -dM -ffreestanding -triple=sparc64-none-none < /dev/null | FileCheck -check-prefix SPARCV9 %s +// SPARCV9:#define __INTMAX_TYPE__ long int +// SPARCV9:#define __INTPTR_TYPE__ long int +// SPARCV9:#define __LONG_MAX__ 9223372036854775807L +// SPARCV9:#define __LP64__ 1 +// SPARCV9:#define __SIZEOF_LONG__ 8 +// SPARCV9:#define __SIZEOF_POINTER__ 8 +// // RUN: %clang_cc1 -x c++ -triple i686-pc-linux-gnu -fobjc-runtime=gcc -E -dM < /dev/null | FileCheck -check-prefix GNUSOURCE %s // GNUSOURCE:#define _GNU_SOURCE 1 // -- cgit v1.2.3 From 7247c88d1e41514a41085f83ebf03dd5220e054a Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Wed, 15 May 2013 07:37:26 +0000 Subject: Use only explicit bool conversion operator The most common (non-buggy) case are where such objects are used as return expressions in bool-returning functions or as boolean function arguments. In those cases I've used (& added if necessary) a named function to provide the equivalent (or sometimes negative, depending on convenient wording) test. DiagnosticBuilder kept its implicit conversion operator owing to the prevalent use of it in return statements. One bug was found in ExprConstant.cpp involving a comparison of two PointerUnions (PointerUnion did not previously have an operator==, so instead both operands were converted to bool & then compared). A test is included in test/SemaCXX/constant-expression-cxx1y.cpp for the fix (adding operator== to PointerUnion in LLVM). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181869 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/CanonicalType.h | 2 +- include/clang/AST/DeclFriend.h | 2 +- include/clang/AST/DeclObjC.h | 4 ++-- include/clang/AST/DeclarationName.h | 7 ++++++- include/clang/AST/ExprCXX.h | 2 +- include/clang/AST/ExternalASTSource.h | 7 ++++++- include/clang/AST/NestedNameSpecifier.h | 6 +++++- include/clang/AST/StmtIterator.h | 5 +++-- include/clang/AST/Type.h | 2 +- include/clang/AST/TypeLoc.h | 2 +- include/clang/Analysis/Analyses/FormatString.h | 2 +- include/clang/Analysis/CFG.h | 2 +- include/clang/Basic/Diagnostic.h | 2 +- include/clang/Frontend/ASTUnit.h | 2 +- include/clang/Frontend/CompilerInstance.h | 10 ++++++---- include/clang/Frontend/FrontendAction.h | 2 +- include/clang/Lex/HeaderSearch.h | 2 +- include/clang/Lex/MacroInfo.h | 2 +- include/clang/Lex/ModuleMap.h | 2 +- include/clang/Lex/Preprocessor.h | 2 +- include/clang/Sema/Initialization.h | 2 +- include/clang/Sema/Ownership.h | 2 +- include/clang/Sema/Sema.h | 2 +- include/clang/Sema/TypoCorrection.h | 2 +- include/clang/Serialization/ASTReader.h | 2 +- include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h | 2 +- include/clang/StaticAnalyzer/Core/Checker.h | 2 +- include/clang/StaticAnalyzer/Core/PathSensitive/Store.h | 2 +- lib/AST/ASTDumper.cpp | 2 +- lib/AST/ASTImporter.cpp | 2 +- lib/AST/Decl.cpp | 4 ++-- lib/AST/Expr.cpp | 2 +- lib/AST/ExprConstant.cpp | 2 +- lib/Analysis/CFG.cpp | 2 +- lib/CodeGen/CodeGenFunction.h | 2 +- lib/Lex/HeaderMap.cpp | 2 +- lib/Lex/PPDirectives.cpp | 2 +- lib/Lex/PPLexerChange.cpp | 2 +- lib/Parse/ParseDecl.cpp | 2 +- lib/Rewrite/Core/Rewriter.cpp | 2 +- lib/Sema/SemaDeclCXX.cpp | 2 +- lib/Sema/SemaDeclObjC.cpp | 2 +- lib/Sema/SemaExpr.cpp | 2 +- test/SemaCXX/constant-expression-cxx1y.cpp | 5 +++-- unittests/Tooling/CompilationDatabaseTest.cpp | 4 ++-- 45 files changed, 71 insertions(+), 53 deletions(-) diff --git a/include/clang/AST/CanonicalType.h b/include/clang/AST/CanonicalType.h index 946075739d..9c699b7e0a 100644 --- a/include/clang/AST/CanonicalType.h +++ b/include/clang/AST/CanonicalType.h @@ -81,7 +81,7 @@ public: operator QualType() const { return Stored; } /// \brief Implicit conversion to bool. - operator bool() const { return !isNull(); } + LLVM_EXPLICIT operator bool() const { return !isNull(); } bool isNull() const { return Stored.isNull(); diff --git a/include/clang/AST/DeclFriend.h b/include/clang/AST/DeclFriend.h index 3a12878e74..589178ec6e 100644 --- a/include/clang/AST/DeclFriend.h +++ b/include/clang/AST/DeclFriend.h @@ -228,7 +228,7 @@ inline CXXRecordDecl::friend_iterator CXXRecordDecl::friend_end() const { } inline void CXXRecordDecl::pushFriendDecl(FriendDecl *FD) { - assert(FD->NextFriend == 0 && "friend already has next friend?"); + assert(!FD->NextFriend && "friend already has next friend?"); FD->NextFriend = data().FirstFriend; data().FirstFriend = FD; } diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 40de0135a7..0028240349 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -452,7 +452,7 @@ public: } /// \brief Determine whether this method has a body. - virtual bool hasBody() const { return Body; } + virtual bool hasBody() const { return Body.isValid(); } /// \brief Retrieve the body of this method, if it has one. virtual Stmt *getBody() const; @@ -463,7 +463,7 @@ public: void setBody(Stmt *B) { Body = B; } /// \brief Returns whether this specific method is a definition. - bool isThisDeclarationADefinition() const { return Body; } + bool isThisDeclarationADefinition() const { return hasBody(); } // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } diff --git a/include/clang/AST/DeclarationName.h b/include/clang/AST/DeclarationName.h index e01b80f389..00766c27c1 100644 --- a/include/clang/AST/DeclarationName.h +++ b/include/clang/AST/DeclarationName.h @@ -182,11 +182,16 @@ public: // operator bool() - Evaluates true when this declaration name is // non-empty. - operator bool() const { + LLVM_EXPLICIT operator bool() const { return ((Ptr & PtrMask) != 0) || (reinterpret_cast(Ptr & ~PtrMask)); } + /// \brief Evaluates true when this declaration name is empty. + bool isEmpty() const { + return !*this; + } + /// Predicate functions for querying what type of name this is. bool isIdentifier() const { return getStoredNameKind() == StoredIdentifier; } bool isObjCZeroArgSelector() const { diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 91e5b21eac..a4f296c988 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -1935,7 +1935,7 @@ public: /// \brief Determines whether this member expression actually had /// a C++ nested-name-specifier prior to the name of the member, e.g., /// x->Base::foo. - bool hasQualifier() const { return QualifierLoc; } + bool hasQualifier() const { return QualifierLoc.hasQualifier(); } /// \brief Retrieves the nested-name-specifier that qualifies the type name, /// with source-location information. diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h index 81fcf242b6..b077426e6a 100644 --- a/include/clang/AST/ExternalASTSource.h +++ b/include/clang/AST/ExternalASTSource.h @@ -329,7 +329,12 @@ public: /// \brief Whether this pointer is non-NULL. /// /// This operation does not require the AST node to be deserialized. - operator bool() const { return Ptr != 0; } + LLVM_EXPLICIT operator bool() const { return Ptr != 0; } + + /// \brief Whether this pointer is non-NULL. + /// + /// This operation does not require the AST node to be deserialized. + bool isValid() const { return Ptr != 0; } /// \brief Whether this pointer is currently stored as an offset. bool isOffset() const { return Ptr & 0x01; } diff --git a/include/clang/AST/NestedNameSpecifier.h b/include/clang/AST/NestedNameSpecifier.h index 58f39862b1..b332b153fe 100644 --- a/include/clang/AST/NestedNameSpecifier.h +++ b/include/clang/AST/NestedNameSpecifier.h @@ -231,7 +231,11 @@ public: /// \brief Evalutes true when this nested-name-specifier location is /// non-empty. - operator bool() const { return Qualifier; } + LLVM_EXPLICIT operator bool() const { return Qualifier; } + + /// \brief Evalutes true when this nested-name-specifier location is + /// empty. + bool hasQualifier() const { return Qualifier; } /// \brief Retrieve the nested-name-specifier to which this instance /// refers. diff --git a/include/clang/AST/StmtIterator.h b/include/clang/AST/StmtIterator.h index b933ed0762..fc25fa9a6e 100644 --- a/include/clang/AST/StmtIterator.h +++ b/include/clang/AST/StmtIterator.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_AST_STMT_ITR_H #include "llvm/Support/DataTypes.h" +#include "llvm/Support/Compiler.h" #include #include #include @@ -156,7 +157,7 @@ struct StmtRange : std::pair { : std::pair(begin, end) {} bool empty() const { return first == second; } - operator bool() const { return !empty(); } + LLVM_EXPLICIT operator bool() const { return !empty(); } Stmt *operator->() const { return first.operator->(); } Stmt *&operator*() const { return first.operator*(); } @@ -199,7 +200,7 @@ struct ConstStmtRange : std::pair { : std::pair(begin, end) {} bool empty() const { return first == second; } - operator bool() const { return !empty(); } + LLVM_EXPLICIT operator bool() const { return !empty(); } const Stmt *operator->() const { return first.operator->(); } const Stmt *operator*() const { return first.operator*(); } diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 39f10d3393..93c287fd18 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -441,7 +441,7 @@ public: bool operator==(Qualifiers Other) const { return Mask == Other.Mask; } bool operator!=(Qualifiers Other) const { return Mask != Other.Mask; } - operator bool() const { return hasQualifiers(); } + LLVM_EXPLICIT operator bool() const { return hasQualifiers(); } Qualifiers &operator+=(Qualifiers R) { addQualifiers(R); diff --git a/include/clang/AST/TypeLoc.h b/include/clang/AST/TypeLoc.h index 11cad9bb9d..9e2d3bffb9 100644 --- a/include/clang/AST/TypeLoc.h +++ b/include/clang/AST/TypeLoc.h @@ -90,7 +90,7 @@ public: } bool isNull() const { return !Ty; } - operator bool() const { return Ty; } + LLVM_EXPLICIT operator bool() const { return Ty; } /// \brief Returns the size of type source info data block for the given type. static unsigned getFullDataSizeForType(QualType Ty); diff --git a/include/clang/Analysis/Analyses/FormatString.h b/include/clang/Analysis/Analyses/FormatString.h index 4bd989cf4e..6ad89aec0a 100644 --- a/include/clang/Analysis/Analyses/FormatString.h +++ b/include/clang/Analysis/Analyses/FormatString.h @@ -49,7 +49,7 @@ public: const char *toString() const { return representation; } // Overloaded operators for bool like qualities - operator bool() const { return flag; } + LLVM_EXPLICIT operator bool() const { return flag; } OptionalFlag& operator=(const bool &rhs) { flag = rhs; return *this; // Return a reference to myself. diff --git a/include/clang/Analysis/CFG.h b/include/clang/Analysis/CFG.h index ee0be736dd..01480ed3b4 100644 --- a/include/clang/Analysis/CFG.h +++ b/include/clang/Analysis/CFG.h @@ -269,7 +269,7 @@ public: Stmt &operator*() { return *getStmt(); } const Stmt &operator*() const { return *getStmt(); } - operator bool() const { return getStmt(); } + LLVM_EXPLICIT operator bool() const { return getStmt(); } }; /// CFGBlock - Represents a single basic block in a source-level CFG. diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index 3e125944a3..1354120ea5 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -1212,7 +1212,7 @@ public: ~StoredDiagnostic(); /// \brief Evaluates true when this object stores a diagnostic. - operator bool() const { return Message.size() > 0; } + LLVM_EXPLICIT operator bool() const { return Message.size() > 0; } unsigned getID() const { return ID; } DiagnosticsEngine::Level getLevel() const { return Level; } diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index 9bd0ef31e0..1fc56ce5f3 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -456,7 +456,7 @@ public: void setASTContext(ASTContext *ctx) { Ctx = ctx; } void setPreprocessor(Preprocessor *pp); - bool hasSema() const { return TheSema; } + bool hasSema() const { return TheSema.isValid(); } Sema &getSema() const { assert(TheSema && "ASTUnit does not have a Sema object!"); return *TheSema; diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h index dbd76066b9..ee4850580d 100644 --- a/include/clang/Frontend/CompilerInstance.h +++ b/include/clang/Frontend/CompilerInstance.h @@ -395,7 +395,7 @@ public: /// @name ASTConsumer /// { - bool hasASTConsumer() const { return Consumer != 0; } + bool hasASTConsumer() const { return Consumer.isValid(); } ASTConsumer &getASTConsumer() const { assert(Consumer && "Compiler instance has no AST consumer!"); @@ -413,7 +413,7 @@ public: /// } /// @name Semantic analysis /// { - bool hasSema() const { return TheSema != 0; } + bool hasSema() const { return TheSema.isValid(); } Sema &getSema() const { assert(TheSema && "Compiler instance has no Sema object!"); @@ -433,7 +433,9 @@ public: /// @name Code Completion /// { - bool hasCodeCompletionConsumer() const { return CompletionConsumer != 0; } + bool hasCodeCompletionConsumer() const { + return CompletionConsumer.isValid(); + } CodeCompleteConsumer &getCodeCompletionConsumer() const { assert(CompletionConsumer && @@ -455,7 +457,7 @@ public: /// @name Frontend timer /// { - bool hasFrontendTimer() const { return FrontendTimer != 0; } + bool hasFrontendTimer() const { return FrontendTimer.isValid(); } llvm::Timer &getFrontendTimer() const { assert(FrontendTimer && "Compiler instance has no frontend timer!"); diff --git a/include/clang/Frontend/FrontendAction.h b/include/clang/Frontend/FrontendAction.h index c67be92472..fee8d95a05 100644 --- a/include/clang/Frontend/FrontendAction.h +++ b/include/clang/Frontend/FrontendAction.h @@ -116,7 +116,7 @@ public: bool isCurrentFileAST() const { assert(!CurrentInput.isEmpty() && "No current file!"); - return CurrentASTUnit != 0; + return CurrentASTUnit.isValid(); } const FrontendInputFile &getCurrentInput() const { diff --git a/include/clang/Lex/HeaderSearch.h b/include/clang/Lex/HeaderSearch.h index c46c8ce6ef..498de09496 100644 --- a/include/clang/Lex/HeaderSearch.h +++ b/include/clang/Lex/HeaderSearch.h @@ -264,7 +264,7 @@ public: /// \brief Checks whether the map exists or not. bool HasIncludeAliasMap() const { - return IncludeAliases; + return IncludeAliases.isValid(); } /// \brief Map the source include name to the dest include name. diff --git a/include/clang/Lex/MacroInfo.h b/include/clang/Lex/MacroInfo.h index 64323b7c76..b4ce4db7dd 100644 --- a/include/clang/Lex/MacroInfo.h +++ b/include/clang/Lex/MacroInfo.h @@ -421,7 +421,7 @@ public: bool isValid() const { return DefDirective != 0; } bool isInvalid() const { return !isValid(); } - operator bool() const { return isValid(); } + LLVM_EXPLICIT operator bool() const { return isValid(); } inline DefInfo getPreviousDefinition(bool AllowHidden = false); const DefInfo getPreviousDefinition(bool AllowHidden = false) const { diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 9fe97d0111..bd57ce4877 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -80,7 +80,7 @@ class ModuleMap { // \brief Whether this known header is valid (i.e., it has an // associated module). - operator bool() const { return Storage.getPointer() != 0; } + LLVM_EXPLICIT operator bool() const { return Storage.getPointer() != 0; } }; typedef llvm::DenseMap HeadersMap; diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index c5981777cd..89cb696f62 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -1400,7 +1400,7 @@ private: bool InCachingLexMode() const { // If the Lexer pointers are 0 and IncludeMacroStack is empty, it means // that we are past EOF, not that we are in CachingLex mode. - return CurPPLexer == 0 && CurTokenLexer == 0 && CurPTHLexer == 0 && + return !CurPPLexer && !CurTokenLexer && !CurPTHLexer && !IncludeMacroStack.empty(); } void EnterCachingLexMode(); diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index 58781ac628..3f70672459 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -841,7 +841,7 @@ public: void setSequenceKind(enum SequenceKind SK) { SequenceKind = SK; } /// \brief Determine whether the initialization sequence is valid. - operator bool() const { return !Failed(); } + LLVM_EXPLICIT operator bool() const { return !Failed(); } /// \brief Determine whether the initialization sequence is invalid. bool Failed() const { return SequenceKind == FailedSequence; } diff --git a/include/clang/Sema/Ownership.h b/include/clang/Sema/Ownership.h index c3d1f4e0b7..92c486db36 100644 --- a/include/clang/Sema/Ownership.h +++ b/include/clang/Sema/Ownership.h @@ -65,7 +65,7 @@ namespace clang { Ptr = Traits::getAsVoidPointer(P); } - operator bool() const { return Ptr != 0; } + LLVM_EXPLICIT operator bool() const { return Ptr != 0; } void *getAsOpaquePtr() const { return Ptr; } static OpaquePtr getFromOpaquePtr(void *P) { return OpaquePtr(P); } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index c2300e977e..61b91c5962 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -5994,7 +5994,7 @@ public: /// \brief Determines whether we have exceeded the maximum /// recursive template instantiations. - operator bool() const { return Invalid; } + LLVM_EXPLICIT operator bool() const { return Invalid; } private: Sema &SemaRef; diff --git a/include/clang/Sema/TypoCorrection.h b/include/clang/Sema/TypoCorrection.h index 5fbfba2d5d..59f3417605 100644 --- a/include/clang/Sema/TypoCorrection.h +++ b/include/clang/Sema/TypoCorrection.h @@ -140,7 +140,7 @@ public: } /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName - operator bool() const { return bool(CorrectionName); } + LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); } /// \brief Mark this TypoCorrection as being a keyword. /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index bb59784dff..073baf514c 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -1232,7 +1232,7 @@ public: void setDeserializationListener(ASTDeserializationListener *Listener); /// \brief Determine whether this AST reader has a global index. - bool hasGlobalIndex() const { return GlobalIndex; } + bool hasGlobalIndex() const { return GlobalIndex.isValid(); } /// \brief Attempts to load the global index. /// diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h index a80b5a7a24..09ee005af7 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h @@ -504,7 +504,7 @@ public: } bool hasCallStackHint() { - return (CallStackHint != 0); + return CallStackHint.isValid(); } /// Produce the hint for the given node. The node contains diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h index 0dbaab033d..d56f8e9c1d 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/include/clang/StaticAnalyzer/Core/Checker.h @@ -505,7 +505,7 @@ struct ImplicitNullDerefEvent { struct DefaultBool { bool val; DefaultBool() : val(false) {} - operator bool() const { return val; } + LLVM_EXPLICIT operator bool() const { return val; } DefaultBool &operator=(bool b) { val = b; return *this; } }; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h index b219495d5f..230ce1ea5c 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -231,7 +231,7 @@ public: bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, SVal val); - operator bool() { return First && Binding; } + LLVM_EXPLICIT operator bool() { return First && Binding; } const MemRegion *getRegion() { return Binding; } }; diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 790538fcd0..cd974f0d02 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1329,7 +1329,7 @@ void ASTDumper::dumpStmt(const Stmt *S) { return; } - setMoreChildren(S->children()); + setMoreChildren(!S->children().empty()); ConstStmtVisitor::Visit(S); setMoreChildren(false); for (Stmt::const_child_range CI = S->children(); CI; ++CI) { diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 740b4ff721..204b9d001c 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -2878,7 +2878,7 @@ Decl *ASTNodeImporter::VisitIndirectFieldDecl(IndirectFieldDecl *D) { if (Importer.IsStructurallyEquivalent(D->getType(), FoundField->getType(), - Name)) { + !Name.isEmpty())) { Importer.Imported(D, FoundField); return FoundField; } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 3e4c2cbdb2..b8f478728f 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -2319,7 +2319,7 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { const FunctionDecl *Prev = this; bool FoundBody = false; while ((Prev = Prev->getPreviousDecl())) { - FoundBody |= Prev->Body; + FoundBody |= Prev->Body.isValid(); if (Prev->Body) { // If it's not the case that both 'inline' and 'extern' are @@ -2347,7 +2347,7 @@ bool FunctionDecl::doesDeclarationForceExternallyVisibleDefinition() const { const FunctionDecl *Prev = this; bool FoundBody = false; while ((Prev = Prev->getPreviousDecl())) { - FoundBody |= Prev->Body; + FoundBody |= Prev->Body.isValid(); if (RedeclForcesDefC99(Prev)) return false; } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 15cf5ac4ab..f587dfa93e 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -423,7 +423,7 @@ DeclRefExpr *DeclRefExpr::Create(ASTContext &Context, FoundD = 0; std::size_t Size = sizeof(DeclRefExpr); - if (QualifierLoc != 0) + if (QualifierLoc) Size += sizeof(NestedNameSpecifierLoc); if (FoundD) Size += sizeof(NamedDecl *); diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 2a3efb225b..3741bf12d5 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1809,7 +1809,7 @@ struct CompleteObject { assert(Value && "missing value for complete object"); } - operator bool() const { return Value; } + LLVM_EXPLICIT operator bool() const { return Value; } }; /// Find the designated sub-object of an rvalue. diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 096c7a080b..6eb84ce92c 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -155,7 +155,7 @@ public: return !(*this == rhs); } - operator bool() const { + LLVM_EXPLICIT operator bool() const { return *this != const_iterator(); } diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 6caf1689a9..5a7c305c64 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -2422,7 +2422,7 @@ public: return ConstantEmission(C, false); } - operator bool() const { return ValueAndIsReference.getOpaqueValue() != 0; } + LLVM_EXPLICIT operator bool() const { return ValueAndIsReference.getOpaqueValue() != 0; } bool isReference() const { return ValueAndIsReference.getInt(); } LValue getReferenceLValue(CodeGenFunction &CGF, Expr *refExpr) const { diff --git a/lib/Lex/HeaderMap.cpp b/lib/Lex/HeaderMap.cpp index dcf1f0c70c..478462c3c2 100644 --- a/lib/Lex/HeaderMap.cpp +++ b/lib/Lex/HeaderMap.cpp @@ -82,7 +82,7 @@ const HeaderMap *HeaderMap::Create(const FileEntry *FE, FileManager &FM) { if (FileSize <= sizeof(HMapHeader)) return 0; OwningPtr FileBuffer(FM.getBufferForFile(FE)); - if (FileBuffer == 0) return 0; // Unreadable file? + if (!FileBuffer) return 0; // Unreadable file? const char *FileStart = FileBuffer->getBufferStart(); // We know the file is at least as big as the header, check it now. diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index 50a0cb55f7..ba3291aa39 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -241,7 +241,7 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation IfTokenLoc, bool FoundElse, SourceLocation ElseLoc) { ++NumSkipped; - assert(CurTokenLexer == 0 && CurPPLexer && "Lexing a macro, not a file?"); + assert(!CurTokenLexer && CurPPLexer && "Lexing a macro, not a file?"); CurPPLexer->pushConditionalLevel(IfTokenLoc, /*isSkipping*/false, FoundNonSkipPortion, FoundElse); diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp index be4defe786..a22d67a6ed 100644 --- a/lib/Lex/PPLexerChange.cpp +++ b/lib/Lex/PPLexerChange.cpp @@ -70,7 +70,7 @@ PreprocessorLexer *Preprocessor::getCurrentFileLexer() const { /// start lexing tokens from it instead of the current buffer. void Preprocessor::EnterSourceFile(FileID FID, const DirectoryLookup *CurDir, SourceLocation Loc) { - assert(CurTokenLexer == 0 && "Cannot #include a file inside a macro!"); + assert(!CurTokenLexer && "Cannot #include a file inside a macro!"); ++NumEnteredSourceFiles; if (MaxIncludeStackDepth < IncludeMacroStack.size()) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 6a87b78879..8ad028155a 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2524,7 +2524,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // erroneous: We already checked about that it has no type specifier, and // C++ doesn't have implicit int. Diagnose it as a typo w.r.t. to the // typename. - if (TypeRep == 0) { + if (!TypeRep) { ConsumeToken(); // Eat the scope spec so the identifier is current. ParsedAttributesWithRange Attrs(AttrFactory); if (ParseImplicitInt(DS, &SS, TemplateInfo, AS, DSContext, Attrs)) { diff --git a/lib/Rewrite/Core/Rewriter.cpp b/lib/Rewrite/Core/Rewriter.cpp index c1c6595d16..540e8b27fd 100644 --- a/lib/Rewrite/Core/Rewriter.cpp +++ b/lib/Rewrite/Core/Rewriter.cpp @@ -463,7 +463,7 @@ public: } } - bool ok() { return FileStream; } + bool ok() { return FileStream.isValid(); } raw_ostream &getStream() { return *FileStream; } private: diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index e5e43897ee..437a9da422 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -9307,7 +9307,7 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation, ParmVarDecl *Other = MoveAssignOperator->getParamDecl(0); QualType OtherRefType = Other->getType()-> getAs()->getPointeeType(); - assert(OtherRefType.getQualifiers() == 0 && + assert(!OtherRefType.getQualifiers() && "Bad argument type of defaulted move assignment"); // Our location for everything implicitly-generated. diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 07610585c5..3265ab02dc 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -2945,7 +2945,7 @@ Decl *Sema::ActOnMethodDeclaration( QualType ArgType; TypeSourceInfo *DI; - if (ArgInfo[i].Type == 0) { + if (!ArgInfo[i].Type) { ArgType = Context.getObjCIdType(); DI = 0; } else { diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 2595f2d099..fa2bfd260b 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -4468,7 +4468,7 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, ExprResult Sema::ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty, SourceLocation RParenLoc, Expr *InitExpr) { - assert((Ty != 0) && "ActOnCompoundLiteral(): missing type"); + assert(Ty && "ActOnCompoundLiteral(): missing type"); // FIXME: put back this assert when initializers are worked out. //assert((InitExpr != 0) && "ActOnCompoundLiteral(): missing expression"); diff --git a/test/SemaCXX/constant-expression-cxx1y.cpp b/test/SemaCXX/constant-expression-cxx1y.cpp index 59a972a4fd..ea0c9e6526 100644 --- a/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/test/SemaCXX/constant-expression-cxx1y.cpp @@ -250,11 +250,12 @@ namespace lifetime { } namespace const_modify { - constexpr int modify(int &n) { return n = 1; } // expected-note {{modification of object of const-qualified type 'const int'}} + constexpr int modify(int &n) { return n = 1; } // expected-note 2 {{modification of object of const-qualified type 'const int'}} constexpr int test1() { int k = 0; return modify(k); } - constexpr int test2() { const int k = 0; return modify(const_cast(k)); } // expected-note {{in call}} + constexpr int test2() { const int k = 0; return modify(const_cast(k)); } // expected-note 2 {{in call}} static_assert(test1() == 1, ""); static_assert(test2() == 1, ""); // expected-error {{constant expression}} expected-note {{in call}} + constexpr int i = test2(); // expected-error {{constant expression}} expected-note {{in call}} } namespace null { diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp index c453b056d2..defde91b26 100644 --- a/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/unittests/Tooling/CompilationDatabaseTest.cpp @@ -453,7 +453,7 @@ TEST(ParseFixedCompilationDatabase, ReturnsArgumentsAfterDoubleDash) { const char *Argv[] = { "1", "2", "--\0no-constant-folding", "3", "4" }; OwningPtr Database( FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); - ASSERT_TRUE(Database); + ASSERT_TRUE(Database.isValid()); std::vector Result = Database->getCompileCommands("source"); ASSERT_EQ(1ul, Result.size()); @@ -472,7 +472,7 @@ TEST(ParseFixedCompilationDatabase, ReturnsEmptyCommandLine) { const char *Argv[] = { "1", "2", "--\0no-constant-folding" }; OwningPtr Database( FixedCompilationDatabase::loadFromCommandLine(Argc, Argv)); - ASSERT_TRUE(Database); + ASSERT_TRUE(Database.isValid()); std::vector Result = Database->getCompileCommands("source"); ASSERT_EQ(1ul, Result.size()); -- cgit v1.2.3 From a8fda85af8ddc91366b5522aee3a78a1fed206f7 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Wed, 15 May 2013 07:51:51 +0000 Subject: Improve formatting of function types. The function type detection in r181438 and r181764 detected function types too eagerly. This led to inconsistent formatting of inline assembly and (together with r181687) to an incorrect formatting of calls in macros. Before: #define DEREF_AND_CALL_F(parameter) f (*parameter) After: #define DEREF_AND_CALL_F(parameter) f(*parameter) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181870 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/TokenAnnotator.cpp | 7 ++++--- unittests/Format/FormatTest.cpp | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 11f81fd8ba..e0552de01d 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -155,6 +155,9 @@ private: } if (CurrentToken->is(tok::r_paren)) { + if (CurrentToken->Children.empty() || + !CurrentToken->Children[0].isOneOf(tok::l_paren, tok::l_square)) + Left->DefinesFunctionType = false; if (CurrentToken->Parent->closesScope()) CurrentToken->Parent->MatchingParen->NoMoreTokensOnLevel = true; Left->MatchingParen = CurrentToken; @@ -173,9 +176,7 @@ private: } if (CurrentToken->isOneOf(tok::r_square, tok::r_brace)) return false; - if (Left->Parent && - !Left->Parent->isOneOf(tok::kw_sizeof, tok::kw_alignof) && - CurrentToken->Parent->Type == TT_PointerOrReference && + if (CurrentToken->Parent->Type == TT_PointerOrReference && CurrentToken->Parent->Parent->isOneOf(tok::l_paren, tok::coloncolon)) Left->DefinesFunctionType = true; updateParameterCount(Left, CurrentToken); diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 206343bb69..081303af6a 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -1110,7 +1110,7 @@ TEST_F(FormatTest, FormatsInlineASM) { "asm(\"movq\\t%%rbx, %%rsi\\n\\t\"\n" " \"cpuid\\n\\t\"\n" " \"xchgq\\t%%rbx, %%rsi\\n\\t\"\n" - " : \"=a\" (*rEAX), \"=S\" (*rEBX), \"=c\" (*rECX), \"=d\" (*rEDX)\n" + " : \"=a\"(*rEAX), \"=S\"(*rEBX), \"=c\"(*rECX), \"=d\"(*rEDX)\n" " : \"a\"(value));"); } @@ -2803,9 +2803,9 @@ TEST_F(FormatTest, FormatsFunctionTypes) { verifyGoogleFormat("A;"); verifyGoogleFormat("void* (*a)(int);"); - // Other constructs can look like function types: + // Other constructs can look somewhat like function types: verifyFormat("A a;"); - verifyFormat("A a;"); + verifyFormat("#define DEREF_AND_CALL_F(x) f(*x)"); } TEST_F(FormatTest, BreaksLongDeclarations) { -- cgit v1.2.3 From caf42a3ef114900fd2ab927d1a13b4db000515e8 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Wed, 15 May 2013 08:14:19 +0000 Subject: Remove diagnostics from clang-format. We only ever implemented one and that one is not actually all that helpful (e.g. gets incorrectly triggered by macros). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181871 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Format/Format.h | 6 +----- lib/Format/Format.cpp | 26 +++++--------------------- lib/Format/UnwrappedLineParser.cpp | 12 ++++-------- lib/Format/UnwrappedLineParser.h | 7 +------ test/Format/diagnostic.cpp | 4 ---- unittests/Format/FormatTest.cpp | 4 ++-- 6 files changed, 13 insertions(+), 46 deletions(-) delete mode 100644 test/Format/diagnostic.cpp diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index 47b5bb6907..ba44c04024 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -177,15 +177,11 @@ std::string configurationAsText(const FormatStyle &Style); /// everything that might influence its formatting or might be influenced by its /// formatting. /// -/// \param DiagClient A custom DiagnosticConsumer. Can be 0, in this case -/// diagnostic is output to llvm::errs(). -/// /// Returns the \c Replacements necessary to make all \p Ranges comply with /// \p Style. tooling::Replacements reformat(const FormatStyle &Style, Lexer &Lex, SourceManager &SourceMgr, - std::vector Ranges, - DiagnosticConsumer *DiagClient = 0); + std::vector Ranges); /// \brief Returns the \c LangOpts that the formatter expects you to set. LangOptions getFormattingLangOpts(); diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index e85ecd21c2..6472a1f10d 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -19,11 +19,9 @@ #include "TokenAnnotator.h" #include "UnwrappedLineParser.h" #include "WhitespaceManager.h" -#include "clang/Basic/Diagnostic.h" #include "clang/Basic/OperatorPrecedence.h" #include "clang/Basic/SourceManager.h" #include "clang/Format/Format.h" -#include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Allocator.h" @@ -1140,17 +1138,16 @@ private: class Formatter : public UnwrappedLineConsumer { public: - Formatter(DiagnosticsEngine &Diag, const FormatStyle &Style, Lexer &Lex, - SourceManager &SourceMgr, + Formatter(const FormatStyle &Style, Lexer &Lex, SourceManager &SourceMgr, const std::vector &Ranges) - : Diag(Diag), Style(Style), Lex(Lex), SourceMgr(SourceMgr), + : Style(Style), Lex(Lex), SourceMgr(SourceMgr), Whitespaces(SourceMgr, Style), Ranges(Ranges) {} virtual ~Formatter() {} tooling::Replacements format() { LexerBasedFormatTokenSource Tokens(Lex, SourceMgr); - UnwrappedLineParser Parser(Diag, Style, Tokens, *this); + UnwrappedLineParser Parser(Style, Tokens, *this); bool StructuralError = Parser.parse(); unsigned PreviousEndOfLineColumn = 0; TokenAnnotator Annotator(Style, SourceMgr, Lex, @@ -1529,7 +1526,6 @@ private: } } - DiagnosticsEngine &Diag; FormatStyle Style; Lexer &Lex; SourceManager &SourceMgr; @@ -1540,20 +1536,8 @@ private: tooling::Replacements reformat(const FormatStyle &Style, Lexer &Lex, SourceManager &SourceMgr, - std::vector Ranges, - DiagnosticConsumer *DiagClient) { - IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); - OwningPtr DiagPrinter; - if (DiagClient == 0) { - DiagPrinter.reset(new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts)); - DiagPrinter->BeginSourceFile(Lex.getLangOpts(), Lex.getPP()); - DiagClient = DiagPrinter.get(); - } - DiagnosticsEngine Diagnostics( - IntrusiveRefCntPtr(new DiagnosticIDs()), &*DiagOpts, - DiagClient, false); - Diagnostics.setSourceManager(&SourceMgr); - Formatter formatter(Diagnostics, Style, Lex, SourceMgr, Ranges); + std::vector Ranges) { + Formatter formatter(Style, Lex, SourceMgr, Ranges); return formatter.format(); } diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index a08790d80d..4138bb98ae 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -16,7 +16,6 @@ #define DEBUG_TYPE "format-parser" #include "UnwrappedLineParser.h" -#include "clang/Basic/Diagnostic.h" #include "llvm/Support/Debug.h" namespace clang { @@ -125,11 +124,11 @@ private: UnwrappedLine *PreBlockLine; }; -UnwrappedLineParser::UnwrappedLineParser( - clang::DiagnosticsEngine &Diag, const FormatStyle &Style, - FormatTokenSource &Tokens, UnwrappedLineConsumer &Callback) +UnwrappedLineParser::UnwrappedLineParser(const FormatStyle &Style, + FormatTokenSource &Tokens, + UnwrappedLineConsumer &Callback) : Line(new UnwrappedLine), MustBreakBeforeNextToken(false), - CurrentLines(&Lines), StructuralError(false), Diag(Diag), Style(Style), + CurrentLines(&Lines), StructuralError(false), Style(Style), Tokens(&Tokens), Callback(Callback) {} bool UnwrappedLineParser::parse() { @@ -173,9 +172,6 @@ void UnwrappedLineParser::parseLevel(bool HasOpeningBrace) { case tok::r_brace: if (HasOpeningBrace) return; - Diag.Report(FormatTok.Tok.getLocation(), - Diag.getCustomDiagID(clang::DiagnosticsEngine::Error, - "unexpected '}'")); StructuralError = true; nextToken(); addUnwrappedLine(); diff --git a/lib/Format/UnwrappedLineParser.h b/lib/Format/UnwrappedLineParser.h index 0c618e24d4..82e93bef48 100644 --- a/lib/Format/UnwrappedLineParser.h +++ b/lib/Format/UnwrappedLineParser.h @@ -23,9 +23,6 @@ #include namespace clang { - -class DiagnosticsEngine; - namespace format { /// \brief A wrapper around a \c Token storing information about the @@ -129,8 +126,7 @@ public: class UnwrappedLineParser { public: - UnwrappedLineParser(clang::DiagnosticsEngine &Diag, const FormatStyle &Style, - FormatTokenSource &Tokens, + UnwrappedLineParser(const FormatStyle &Style, FormatTokenSource &Tokens, UnwrappedLineConsumer &Callback); /// Returns true in case of a structural error. @@ -203,7 +199,6 @@ private: // indentation levels. bool StructuralError; - clang::DiagnosticsEngine &Diag; const FormatStyle &Style; FormatTokenSource *Tokens; UnwrappedLineConsumer &Callback; diff --git a/test/Format/diagnostic.cpp b/test/Format/diagnostic.cpp deleted file mode 100644 index 2e930ee5b7..0000000000 --- a/test/Format/diagnostic.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// RUN: clang-format 2>&1 >/dev/null %s |FileCheck %s - -} -// CHECK: diagnostic.cpp:[[@LINE-1]]:1: error: unexpected '}' diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 081303af6a..b804577d1c 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -32,8 +32,8 @@ protected: CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); Lexer Lex(ID, Context.Sources.getBuffer(ID), Context.Sources, getFormattingLangOpts()); - tooling::Replacements Replace = reformat( - Style, Lex, Context.Sources, Ranges, new IgnoringDiagConsumer()); + tooling::Replacements Replace = + reformat(Style, Lex, Context.Sources, Ranges); ReplacementCount = Replace.size(); EXPECT_TRUE(applyAllReplacements(Replace, Context.Rewrite)); DEBUG(llvm::errs() << "\n" << Context.getRewrittenText(ID) << "\n\n"); -- cgit v1.2.3 From 9f3216199484a95fd765caa6555fd44e5c0ed1ea Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Wed, 15 May 2013 08:30:06 +0000 Subject: Don't merge one-line functions in weird brace styles. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181872 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 3 ++- unittests/Format/FormatTest.cpp | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 6472a1f10d..99521ea8cc 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -1341,7 +1341,8 @@ private: if (I + 1 == E || (I + 1)->Type == LT_Invalid) return; - if (I->Last->is(tok::l_brace)) { + if (I->Last->is(tok::l_brace) && + Style.BreakBeforeBraces == FormatStyle::BS_Attach) { tryMergeSimpleBlock(I, E, Limit); } else if (I->First.is(tok::kw_if)) { tryMergeSimpleIf(I, E, Limit); diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index b804577d1c..69a007834c 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -4117,6 +4117,10 @@ TEST_F(FormatTest, LinuxBraceBreaking) { " b();\n" " }\n" " }\n" + " void g()\n" + " {\n" + " return;\n" + " }\n" "}\n" "}", BreakBeforeBrace); @@ -4134,6 +4138,10 @@ TEST_F(FormatTest, StroustrupBraceBreaking) { " b();\n" " }\n" " }\n" + " void g()\n" + " {\n" + " return;\n" + " }\n" "}\n" "}", BreakBeforeBrace); -- cgit v1.2.3 From 33f4b90476f24d269cac04915d951d8f05bd3a77 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Wed, 15 May 2013 09:35:08 +0000 Subject: Break function declarations after multi-line return types. Before: template SomeLoooooooooooooooooooooongType< typename some_namespace::SomeOtherType::Type> Function() {} After: template SomeLoooooooooooooooooooooongType< typename some_namespace::SomeOtherType::Type> Function() {} git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181877 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 7 +++++++ unittests/Format/FormatTest.cpp | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 99521ea8cc..9e882157aa 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -504,6 +504,8 @@ private: !State.Stack.back().AvoidBinPacking) || Previous.Type == TT_BinaryOperator) State.Stack.back().BreakBeforeParameter = false; + if (Previous.Type == TT_TemplateCloser && State.ParenLevel == 0) + State.Stack.back().BreakBeforeParameter = false; if (!DryRun) { unsigned NewLines = 1; @@ -531,6 +533,7 @@ private: } const AnnotatedToken *TokenBefore = Current.getPreviousNoneComment(); if (TokenBefore && !TokenBefore->isOneOf(tok::comma, tok::semi) && + TokenBefore->Type != TT_TemplateCloser && TokenBefore->Type != TT_BinaryOperator && !TokenBefore->opensScope()) State.Stack.back().BreakBeforeParameter = true; @@ -1017,6 +1020,10 @@ private: getRemainingLength(State) + State.Column > getColumnLimit() && State.ParenLevel < State.StartOfLineLevel) return true; + + if (Current.Type == TT_StartOfName && Line.MightBeFunctionDecl && + State.Stack.back().BreakBeforeParameter && State.ParenLevel == 0) + return true; return false; } diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 69a007834c..20a32e740e 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -2002,7 +2002,7 @@ TEST_F(FormatTest, FormatsBuilderPattern) { " .StartsWith(\".eh_frame\", ORDER_EH_FRAME).StartsWith(\".init\", ORDER_INIT)\n" " .StartsWith(\".fini\", ORDER_FINI).StartsWith(\".hash\", ORDER_HASH)\n" " .Default(ORDER_TEXT);\n"); - + verifyFormat("return aaaaaaaaaaaaaaaaa->aaaaa().aaaaaaaaaaaaa().aaaaaa() <\n" " aaaaaaaaaaaaaaa->aaaaa().aaaaaaaaaaaaa().aaaaaa();"); verifyFormat( @@ -2835,6 +2835,10 @@ TEST_F(FormatTest, BreaksLongDeclarations) { " ReallyReallyLongParameterName,\n" " const SomeType &\n" " AnotherLongParameterName) {}"); + verifyFormat("template \n" + "SomeLoooooooooooooooooooooongType<\n" + " typename some_namespace::SomeOtherType::Type>\n" + "Function() {}"); verifyFormat( "aaaaaaaaaaaaaaaa::aaaaaaaaaaaaaaaa\n" " aaaaaaaaaaaaaaaaaaaaaaa;"); -- cgit v1.2.3 From 0ff5074f37a66bca244a9d5d0da050ff68693ce2 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Wed, 15 May 2013 11:03:04 +0000 Subject: Better diagnostics for string initialization. This commit improves Clang's diagnostics for string initialization. Where it would previously say: /tmp/a.c:3:9: error: array initializer must be an initializer list wchar_t s[] = "Hi"; ^ /tmp/a.c:4:6: error: array initializer must be an initializer list or string literal char t[] = L"Hi"; ^ It will now say /tmp/a.c:3:9: error: initializing wide char array with non-wide string literal wchar_t s[] = "Hi"; ^ /tmp/a.c:4:6: error: initializing char array with wide string literal char t[] = L"Hi"; ^ As a bonus, it also fixes the fact that Clang would previously reject this valid C11 code: char16_t s[] = u"hi"; char32_t t[] = U"hi"; because it would only recognize the built-in types for char16_t and char32_t, which do not exist in C. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181880 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 8 +- include/clang/Sema/Initialization.h | 10 ++ lib/Sema/SemaInit.cpp | 167 ++++++++++++++++++++++------- test/Lexer/char-literal.cpp | 4 - test/Sema/ms-wchar.c | 5 +- test/Sema/string-init.c | 51 +++++++++ test/Sema/wchar.c | 4 +- test/SemaCXX/ms-wchar.cpp | 3 + test/SemaCXX/string-init.cpp | 40 +++++++ 9 files changed, 248 insertions(+), 44 deletions(-) create mode 100644 test/Sema/string-init.c create mode 100644 test/SemaCXX/string-init.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index db4696767a..bac681e871 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4282,7 +4282,13 @@ def err_typecheck_incomplete_array_needs_initializer : Error< "or an initializer">; def err_array_init_not_init_list : Error< "array initializer must be an initializer " - "list%select{| or string literal}0">; + "list%select{| or string literal| or wide string literal}0">; +def err_array_init_narrow_string_into_wchar : Error< + "initializing wide char array with non-wide string literal">; +def err_array_init_wide_string_into_char : Error< + "initializing char array with wide string literal">; +def err_array_init_incompat_wide_string_into_wchar : Error< + "initializing wide char array with incompatible wide string literal">; def err_array_init_different_type : Error< "cannot initialize array %diff{of type $ with array of type $|" "with different type of array}0,1">; diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index 3f70672459..727ceebe8b 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -706,6 +706,16 @@ public: /// \brief Array must be initialized with an initializer list or a /// string literal. FK_ArrayNeedsInitListOrStringLiteral, + /// \brief Array must be initialized with an initializer list or a + /// wide string literal. + FK_ArrayNeedsInitListOrWideStringLiteral, + /// \brief Initializing a wide char array with narrow string literal. + FK_NarrowStringIntoWideCharArray, + /// \brief Initializing char array with wide string literal. + FK_WideStringIntoCharArray, + /// \brief Initializing wide char array with incompatible wide string + /// literal. + FK_IncompatWideStringIntoWideChar, /// \brief Array type mismatch. FK_ArrayTypeMismatch, /// \brief Non-constant array initializer diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 7016e565da..a3b78787e4 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -32,54 +32,99 @@ using namespace clang; // Sema Initialization Checking //===----------------------------------------------------------------------===// -static Expr *IsStringInit(Expr *Init, const ArrayType *AT, - ASTContext &Context) { +/// \brief Check whether T is compatible with a wide character type (wchar_t, +/// char16_t or char32_t). +static bool IsWideCharCompatible(QualType T, ASTContext &Context) { + if (Context.typesAreCompatible(Context.getWideCharType(), T)) + return true; + if (Context.getLangOpts().CPlusPlus || Context.getLangOpts().C11) { + return Context.typesAreCompatible(Context.Char16Ty, T) || + Context.typesAreCompatible(Context.Char32Ty, T); + } + return false; +} + +enum StringInitFailureKind { + SIF_None, + SIF_NarrowStringIntoWideChar, + SIF_WideStringIntoChar, + SIF_IncompatWideStringIntoWideChar, + SIF_Other +}; + +/// \brief Check whether the array of type AT can be initialized by the Init +/// expression by means of string initialization. Returns SIF_None if so, +/// otherwise returns a StringInitFailureKind that describes why the +/// initialization would not work. +static StringInitFailureKind IsStringInit(Expr *Init, const ArrayType *AT, + ASTContext &Context) { if (!isa(AT) && !isa(AT)) - return 0; + return SIF_Other; // See if this is a string literal or @encode. Init = Init->IgnoreParens(); // Handle @encode, which is a narrow string. if (isa(Init) && AT->getElementType()->isCharType()) - return Init; + return SIF_None; // Otherwise we can only handle string literals. StringLiteral *SL = dyn_cast(Init); - if (SL == 0) return 0; + if (SL == 0) + return SIF_Other; - QualType ElemTy = Context.getCanonicalType(AT->getElementType()); + const QualType ElemTy = + Context.getCanonicalType(AT->getElementType()).getUnqualifiedType(); switch (SL->getKind()) { case StringLiteral::Ascii: case StringLiteral::UTF8: // char array can be initialized with a narrow string. // Only allow char x[] = "foo"; not char x[] = L"foo"; - return ElemTy->isCharType() ? Init : 0; + if (ElemTy->isCharType()) + return SIF_None; + if (IsWideCharCompatible(ElemTy, Context)) + return SIF_NarrowStringIntoWideChar; + return SIF_Other; + // C99 6.7.8p15 (with correction from DR343), or C11 6.7.9p15: + // "An array with element type compatible with a qualified or unqualified + // version of wchar_t, char16_t, or char32_t may be initialized by a wide + // string literal with the corresponding encoding prefix (L, u, or U, + // respectively), optionally enclosed in braces. case StringLiteral::UTF16: - return ElemTy->isChar16Type() ? Init : 0; + if (Context.typesAreCompatible(Context.Char16Ty, ElemTy)) + return SIF_None; + if (ElemTy->isCharType()) + return SIF_WideStringIntoChar; + if (IsWideCharCompatible(ElemTy, Context)) + return SIF_IncompatWideStringIntoWideChar; + return SIF_Other; case StringLiteral::UTF32: - return ElemTy->isChar32Type() ? Init : 0; + if (Context.typesAreCompatible(Context.Char32Ty, ElemTy)) + return SIF_None; + if (ElemTy->isCharType()) + return SIF_WideStringIntoChar; + if (IsWideCharCompatible(ElemTy, Context)) + return SIF_IncompatWideStringIntoWideChar; + return SIF_Other; case StringLiteral::Wide: - // wchar_t array can be initialized with a wide string: C99 6.7.8p15 (with - // correction from DR343): "An array with element type compatible with a - // qualified or unqualified version of wchar_t may be initialized by a wide - // string literal, optionally enclosed in braces." - if (Context.typesAreCompatible(Context.getWideCharType(), - ElemTy.getUnqualifiedType())) - return Init; - - return 0; + if (Context.typesAreCompatible(Context.getWideCharType(), ElemTy)) + return SIF_None; + if (ElemTy->isCharType()) + return SIF_WideStringIntoChar; + if (IsWideCharCompatible(ElemTy, Context)) + return SIF_IncompatWideStringIntoWideChar; + return SIF_Other; } llvm_unreachable("missed a StringLiteral kind?"); } -static Expr *IsStringInit(Expr *init, QualType declType, ASTContext &Context) { +static bool IsStringInit(Expr* init, QualType declType, ASTContext& Context) { const ArrayType *arrayType = Context.getAsArrayType(declType); - if (!arrayType) return 0; - - return IsStringInit(init, arrayType, Context); + if (!arrayType) + return false; + return IsStringInit(init, arrayType, Context) == SIF_None; } /// Update the type of a string literal, including any surrounding parentheses, @@ -806,10 +851,10 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, // array member. There's nothing we can do with the completed // type here, though. - if (Expr *Str = IsStringInit(expr, arrayType, SemaRef.Context)) { + if (IsStringInit(expr, arrayType, SemaRef.Context) == SIF_None) { if (!VerifyOnly) { - CheckStringInit(Str, ElemType, arrayType, SemaRef); - UpdateStructuredListElement(StructuredList, StructuredIndex, Str); + CheckStringInit(expr, ElemType, arrayType, SemaRef); + UpdateStructuredListElement(StructuredList, StructuredIndex, expr); } ++Index; return; @@ -1189,16 +1234,17 @@ void InitListChecker::CheckArrayType(const InitializedEntity &Entity, // Check for the special-case of initializing an array with a string. if (Index < IList->getNumInits()) { - if (Expr *Str = IsStringInit(IList->getInit(Index), arrayType, - SemaRef.Context)) { + if (IsStringInit(IList->getInit(Index), arrayType, SemaRef.Context) == + SIF_None) { // We place the string literal directly into the resulting // initializer list. This is the only place where the structure // of the structured initializer list doesn't match exactly, // because doing so would involve allocating one character // constant for each string. if (!VerifyOnly) { - CheckStringInit(Str, DeclType, arrayType, SemaRef); - UpdateStructuredListElement(StructuredList, StructuredIndex, Str); + CheckStringInit(IList->getInit(Index), DeclType, arrayType, SemaRef); + UpdateStructuredListElement(StructuredList, StructuredIndex, + IList->getInit(Index)); StructuredList->resizeInits(SemaRef.Context, StructuredIndex); } ++Index; @@ -2507,6 +2553,10 @@ bool InitializationSequence::isAmbiguous() const { case FK_TooManyInitsForReference: case FK_ArrayNeedsInitList: case FK_ArrayNeedsInitListOrStringLiteral: + case FK_ArrayNeedsInitListOrWideStringLiteral: + case FK_NarrowStringIntoWideCharArray: + case FK_WideStringIntoCharArray: + case FK_IncompatWideStringIntoWideChar: case FK_AddressOfOverloadFailed: // FIXME: Could do better case FK_NonConstLValueReferenceBindingToTemporary: case FK_NonConstLValueReferenceBindingToUnrelated: @@ -4278,9 +4328,23 @@ InitializationSequence::InitializationSequence(Sema &S, return; } - if (Initializer && IsStringInit(Initializer, DestAT, Context)) { - TryStringLiteralInitialization(S, Entity, Kind, Initializer, *this); - return; + if (Initializer) { + switch (IsStringInit(Initializer, DestAT, Context)) { + case SIF_None: + TryStringLiteralInitialization(S, Entity, Kind, Initializer, *this); + return; + case SIF_NarrowStringIntoWideChar: + SetFailed(FK_NarrowStringIntoWideCharArray); + return; + case SIF_WideStringIntoChar: + SetFailed(FK_WideStringIntoCharArray); + return; + case SIF_IncompatWideStringIntoWideChar: + SetFailed(FK_IncompatWideStringIntoWideChar); + return; + case SIF_Other: + break; + } } // Note: as an GNU C extension, we allow initialization of an @@ -4307,8 +4371,10 @@ InitializationSequence::InitializationSequence(Sema &S, TryListInitialization(S, Entity, Kind, cast(Initializer), *this); AddParenthesizedArrayInitStep(DestType); - } else if (DestAT->getElementType()->isAnyCharacterType()) + } else if (DestAT->getElementType()->isCharType()) SetFailed(FK_ArrayNeedsInitListOrStringLiteral); + else if (IsWideCharCompatible(DestAT->getElementType(), Context)) + SetFailed(FK_ArrayNeedsInitListOrWideStringLiteral); else SetFailed(FK_ArrayNeedsInitList); @@ -5816,11 +5882,24 @@ bool InitializationSequence::Diagnose(Sema &S, break; case FK_ArrayNeedsInitList: + S.Diag(Kind.getLocation(), diag::err_array_init_not_init_list) << 0; + break; case FK_ArrayNeedsInitListOrStringLiteral: - S.Diag(Kind.getLocation(), diag::err_array_init_not_init_list) - << (Failure == FK_ArrayNeedsInitListOrStringLiteral); + S.Diag(Kind.getLocation(), diag::err_array_init_not_init_list) << 1; + break; + case FK_ArrayNeedsInitListOrWideStringLiteral: + S.Diag(Kind.getLocation(), diag::err_array_init_not_init_list) << 2; + break; + case FK_NarrowStringIntoWideCharArray: + S.Diag(Kind.getLocation(), diag::err_array_init_narrow_string_into_wchar); + break; + case FK_WideStringIntoCharArray: + S.Diag(Kind.getLocation(), diag::err_array_init_wide_string_into_char); + break; + case FK_IncompatWideStringIntoWideChar: + S.Diag(Kind.getLocation(), + diag::err_array_init_incompat_wide_string_into_wchar); break; - case FK_ArrayTypeMismatch: case FK_NonConstantArrayInit: S.Diag(Kind.getLocation(), @@ -6192,6 +6271,22 @@ void InitializationSequence::dump(raw_ostream &OS) const { OS << "array requires initializer list or string literal"; break; + case FK_ArrayNeedsInitListOrWideStringLiteral: + OS << "array requires initializer list or wide string literal"; + break; + + case FK_NarrowStringIntoWideCharArray: + OS << "narrow string into wide char array"; + break; + + case FK_WideStringIntoCharArray: + OS << "wide string into char array"; + break; + + case FK_IncompatWideStringIntoWideChar: + OS << "incompatible wide string into wide char array"; + break; + case FK_ArrayTypeMismatch: OS << "array type mismatch"; break; diff --git a/test/Lexer/char-literal.cpp b/test/Lexer/char-literal.cpp index b2fab34e44..1cd14a9b01 100644 --- a/test/Lexer/char-literal.cpp +++ b/test/Lexer/char-literal.cpp @@ -36,8 +36,4 @@ char16_t p[2] = u"\U0000FFFF"; char16_t q[2] = u"\U00010000"; #ifdef __cplusplus // expected-error@-2 {{too long}} -#else -// FIXME: The above should be accepted in C11 mode. -// expected-error@-6 {{must be an initializer list}} -// expected-error@-6 {{must be an initializer list}} #endif diff --git a/test/Sema/ms-wchar.c b/test/Sema/ms-wchar.c index 52a736c640..febaf283b3 100644 --- a/test/Sema/ms-wchar.c +++ b/test/Sema/ms-wchar.c @@ -12,4 +12,7 @@ __wchar_t g = L'a'; // expected-note {{previous}} unsigned short g; // expected-error {{redefinition of 'g' with a different type: 'unsigned short' vs '__wchar_t'}} // The type of a wide string literal is actually not __wchar_t. -__wchar_t s[] = L"Hello world!"; // expected-error {{array initializer must be an initializer list}} +__wchar_t s[] = L"Hello world!"; // expected-error-re {{array initializer must be an initializer list$}} + +// Do not suggest initializing with a string here, because it would not work. +__wchar_t t[] = 1; // expected-error-re {{array initializer must be an initializer list$}} diff --git a/test/Sema/string-init.c b/test/Sema/string-init.c new file mode 100644 index 0000000000..96ee360e44 --- /dev/null +++ b/test/Sema/string-init.c @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -std=c11 -fsyntax-only -triple x86_64-pc-linux -verify %s + +// Note: these match the types specified by the target above. +typedef int wchar_t; +typedef unsigned short char16_t; +typedef unsigned int char32_t; + +void f() { + char a1[] = "a"; // No error. + char a2[] = u8"a"; // No error. + char a3[] = u"a"; // expected-error{{initializing char array with wide string literal}} + char a4[] = U"a"; // expected-error{{initializing char array with wide string literal}} + char a5[] = L"a"; // expected-error{{initializing char array with wide string literal}} + + wchar_t b1[] = "a"; // expected-error{{initializing wide char array with non-wide string literal}} + wchar_t b2[] = u8"a"; // expected-error{{initializing wide char array with non-wide string literal}} + wchar_t b3[] = u"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + wchar_t b4[] = U"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + wchar_t b5[] = L"a"; // No error. + + char16_t c1[] = "a"; // expected-error{{initializing wide char array with non-wide string literal}} + char16_t c2[] = u8"a"; // expected-error{{initializing wide char array with non-wide string literal}} + char16_t c3[] = u"a"; // No error. + char16_t c4[] = U"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + char16_t c5[] = L"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + + char32_t d1[] = "a"; // expected-error{{initializing wide char array with non-wide string literal}} + char32_t d2[] = u8"a"; // expected-error{{initializing wide char array with non-wide string literal}} + char32_t d3[] = u"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + char32_t d4[] = U"a"; // No error. + char32_t d5[] = L"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + + int e1[] = "a"; // expected-error{{initializing wide char array with non-wide string literal}} + int e2[] = u8"a"; // expected-error{{initializing wide char array with non-wide string literal}} + int e3[] = u"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + int e4[] = U"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + int e5[] = L"a"; // No error. + + long f1[] = "a"; // expected-error{{array initializer must be an initializer list}} + long f2[] = u8"a"; // expected-error{{array initializer must be an initializer list}}} + long f3[] = u"a"; // expected-error{{array initializer must be an initializer list}} + long f4[] = U"a"; // expected-error{{array initializer must be an initializer list}} + long f5[] = L"a"; // expected-error{{array initializer must be an initializer list}} +} + +void g() { + char a[] = 1; // expected-error{{array initializer must be an initializer list or string literal}} + wchar_t b[] = 1; // expected-error{{array initializer must be an initializer list or wide string literal}} + char16_t c[] = 1; // expected-error{{array initializer must be an initializer list or wide string literal}} + char32_t d[] = 1; // expected-error{{array initializer must be an initializer list or wide string literal}} +} diff --git a/test/Sema/wchar.c b/test/Sema/wchar.c index 816245f3c0..13c2f5855d 100644 --- a/test/Sema/wchar.c +++ b/test/Sema/wchar.c @@ -19,6 +19,6 @@ int check_wchar_size[sizeof(*L"") == sizeof(wchar_t) ? 1 : -1]; void foo() { WCHAR_T_TYPE t1[] = L"x"; wchar_t tab[] = L"x"; - WCHAR_T_TYPE t2[] = "x"; // expected-error {{initializer}} - char t3[] = L"x"; // expected-error {{initializer}} + WCHAR_T_TYPE t2[] = "x"; // expected-error {{initializing wide char array with non-wide string literal}} + char t3[] = L"x"; // expected-error {{initializing char array with wide string literal}} } diff --git a/test/SemaCXX/ms-wchar.cpp b/test/SemaCXX/ms-wchar.cpp index 2cbf745d33..878d8cadce 100644 --- a/test/SemaCXX/ms-wchar.cpp +++ b/test/SemaCXX/ms-wchar.cpp @@ -7,3 +7,6 @@ __wchar_t g = L'a'; __wchar_t s[] = L"Hello world!"; unsigned short t[] = L"Hello world!"; // expected-error{{array initializer must be an initializer list}} + +wchar_t u[] = 1; // expected-error{{array initializer must be an initializer list or wide string literal}} +__wchar_t v[] = 1; // expected-error{{array initializer must be an initializer list or wide string literal}} diff --git a/test/SemaCXX/string-init.cpp b/test/SemaCXX/string-init.cpp new file mode 100644 index 0000000000..7e62d1855a --- /dev/null +++ b/test/SemaCXX/string-init.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s + +void f() { + char a1[] = "a"; // No error. + char a2[] = u8"a"; // No error. + char a3[] = u"a"; // expected-error{{initializing char array with wide string literal}} + char a4[] = U"a"; // expected-error{{initializing char array with wide string literal}} + char a5[] = L"a"; // expected-error{{initializing char array with wide string literal}} + + wchar_t b1[] = "a"; // expected-error{{initializing wide char array with non-wide string literal}} + wchar_t b2[] = u8"a"; // expected-error{{initializing wide char array with non-wide string literal}} + wchar_t b3[] = u"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + wchar_t b4[] = U"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + wchar_t b5[] = L"a"; // No error. + + char16_t c1[] = "a"; // expected-error{{initializing wide char array with non-wide string literal}} + char16_t c2[] = u8"a"; // expected-error{{initializing wide char array with non-wide string literal}} + char16_t c3[] = u"a"; // No error. + char16_t c4[] = U"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + char16_t c5[] = L"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + + char32_t d1[] = "a"; // expected-error{{initializing wide char array with non-wide string literal}} + char32_t d2[] = u8"a"; // expected-error{{initializing wide char array with non-wide string literal}} + char32_t d3[] = u"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + char32_t d4[] = U"a"; // No error. + char32_t d5[] = L"a"; // expected-error{{initializing wide char array with incompatible wide string literal}} + + int e1[] = "a"; // expected-error{{array initializer must be an initializer list}} + int e2[] = u8"a"; // expected-error{{array initializer must be an initializer list}} + int e3[] = u"a"; // expected-error{{array initializer must be an initializer list}} + int e4[] = U"a"; // expected-error{{array initializer must be an initializer list}} + int e5[] = L"a"; // expected-error{{array initializer must be an initializer list}} +} + +void g() { + char a[] = 1; // expected-error{{array initializer must be an initializer list or string literal}} + wchar_t b[] = 1; // expected-error{{array initializer must be an initializer list or wide string literal}} + char16_t c[] = 1; // expected-error{{array initializer must be an initializer list or wide string literal}} + char32_t d[] = 1; // expected-error{{array initializer must be an initializer list or wide string literal}} +} -- cgit v1.2.3 From 5d823e3a00a3e21a0823288e6dee26a93758332b Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Wed, 15 May 2013 13:46:48 +0000 Subject: Improve recognition of template definitions. In the long run, this will probably be better fixed by a proper expression parser.. Before: template Matcher(const Matcher & Other, typename enable_if_c < is_base_of::value && !is_same::value > ::type * = 0) : Implementation(new ImplicitCastMatcher(Other)) {} After: template Matcher(const Matcher & Other, typename enable_if_c::value && !is_same::value>::type * = 0) : Implementation(new ImplicitCastMatcher(Other)) {} git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181884 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/TokenAnnotator.cpp | 6 ++++-- unittests/Format/FormatTest.cpp | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index e0552de01d..21518c6824 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -101,8 +101,10 @@ private: return true; } if (CurrentToken->isOneOf(tok::r_paren, tok::r_square, tok::r_brace, - tok::pipepipe, tok::ampamp, tok::question, - tok::colon)) + tok::question, tok::colon)) + return false; + if (CurrentToken->isOneOf(tok::pipepipe, tok::ampamp) && + Line.First.isNot(tok::kw_template)) return false; updateParameterCount(Left, CurrentToken); if (!consumeToken()) diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 20a32e740e..499388e6ba 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -3077,6 +3077,13 @@ TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) { verifyFormat("class A::B::C {\n} n;"); // Template definitions. + verifyFormat( + "template \n" + "Matcher(const Matcher &Other,\n" + " typename enable_if_c::value &&\n" + " !is_same::value>::type * = 0)\n" + " : Implementation(new ImplicitCastMatcher(Other)) {}"); + // FIXME: This is still incorrectly handled at the formatter side. verifyFormat("template <> struct X < 15, i < 3 && 42 < 50 && 33<28> {\n};"); -- cgit v1.2.3 From 5be59ba8c9fb291f83970f0ebddceda70088bb28 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Wed, 15 May 2013 14:09:55 +0000 Subject: Don't put short namespace on a single line. Before: namespace abc { class SomeClass; } namespace def { void someFunction() {} } After: namespace abc { class Def; } namespace def { void someFunction() {} } Rationale: a) Having anything other than forward declaration on the same line as a namespace looks confusing. b) Formatting namespace-forward-declaration-combinations different from other stuff is inconsistent. c) Wasting vertical space close to such forward declarations really does not affect readability. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181887 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 9 ++++++--- unittests/Format/FormatTest.cpp | 11 +++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 9e882157aa..9fd8ac6916 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -1348,8 +1348,7 @@ private: if (I + 1 == E || (I + 1)->Type == LT_Invalid) return; - if (I->Last->is(tok::l_brace) && - Style.BreakBeforeBraces == FormatStyle::BS_Attach) { + if (I->Last->is(tok::l_brace)) { tryMergeSimpleBlock(I, E, Limit); } else if (I->First.is(tok::kw_if)) { tryMergeSimpleIf(I, E, Limit); @@ -1402,13 +1401,17 @@ private: void tryMergeSimpleBlock(std::vector::iterator &I, std::vector::iterator E, unsigned Limit) { + // No merging if the brace already is on the next line. + if (Style.BreakBeforeBraces != FormatStyle::BS_Attach) + return; + // First, check that the current line allows merging. This is the case if // we're not in a control flow statement and the last token is an opening // brace. AnnotatedLine &Line = *I; if (Line.First.isOneOf(tok::kw_if, tok::kw_while, tok::kw_do, tok::r_brace, tok::kw_else, tok::kw_try, tok::kw_catch, - tok::kw_for, + tok::kw_for, tok::kw_namespace, // This gets rid of all ObjC @ keywords and methods. tok::at, tok::minus, tok::plus)) return; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 499388e6ba..58dbffd863 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -745,11 +745,11 @@ TEST_F(FormatTest, SplitsLongCxxComments) { } TEST_F(FormatTest, ParsesCommentsAdjacentToPPDirectives) { - EXPECT_EQ("namespace {}\n// Test\n#define A", + EXPECT_EQ("namespace {\n}\n// Test\n#define A", format("namespace {}\n // Test\n#define A")); - EXPECT_EQ("namespace {}\n/* Test */\n#define A", + EXPECT_EQ("namespace {\n}\n/* Test */\n#define A", format("namespace {}\n /* Test */\n#define A")); - EXPECT_EQ("namespace {}\n/* Test */ #define A", + EXPECT_EQ("namespace {\n}\n/* Test */ #define A", format("namespace {}\n /* Test */ #define A")); } @@ -2921,7 +2921,10 @@ TEST_F(FormatTest, IncorrectCodeMissingSemicolon) { " return\n" "}", format("void f ( ) { if ( a ) return }")); - EXPECT_EQ("namespace N { void f() }", format("namespace N { void f() }")); + EXPECT_EQ("namespace N {\n" + "void f()\n" + "}", + format("namespace N { void f() }")); EXPECT_EQ("namespace N {\n" "void f() {}\n" "void g()\n" -- cgit v1.2.3 From 92b522db4e3d79e2fab5fd4ce1574afef201ae58 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Wed, 15 May 2013 14:27:33 +0000 Subject: Fix test breakage caused by change in clang-format. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181888 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Index/comment-to-html-xml-conversion.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Index/comment-to-html-xml-conversion.cpp b/test/Index/comment-to-html-xml-conversion.cpp index c770ca8d30..ce64dd40b9 100644 --- a/test/Index/comment-to-html-xml-conversion.cpp +++ b/test/Index/comment-to-html-xml-conversion.cpp @@ -741,11 +741,11 @@ int comment_to_xml_conversion_12; /// Aaa. namespace comment_to_xml_conversion_13 { -// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:11: Namespace=comment_to_xml_conversion_13:{{.*}} FullCommentAsXML=[comment_to_xml_conversion_13c:@N@comment_to_xml_conversion_13namespace comment_to_xml_conversion_13 {} Aaa.] +// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:11: Namespace=comment_to_xml_conversion_13:{{.*}} FullCommentAsXML=[comment_to_xml_conversion_13c:@N@comment_to_xml_conversion_13namespace comment_to_xml_conversion_13 {\n} Aaa.] /// Aaa. namespace comment_to_xml_conversion_14 { -// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:13: Namespace=comment_to_xml_conversion_14:{{.*}} FullCommentAsXML=[comment_to_xml_conversion_14c:@N@comment_to_xml_conversion_13@N@comment_to_xml_conversion_14namespace comment_to_xml_conversion_14 {} Aaa.] +// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:13: Namespace=comment_to_xml_conversion_14:{{.*}} FullCommentAsXML=[comment_to_xml_conversion_14c:@N@comment_to_xml_conversion_13@N@comment_to_xml_conversion_14namespace comment_to_xml_conversion_14 {\n} Aaa.] } } -- cgit v1.2.3 From 14040142a3b3c1029092bc1f7c51e347c3fa8f89 Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Wed, 15 May 2013 15:27:35 +0000 Subject: improve of note message and minor refactoring of my last patch (r181847). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181896 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 2 +- lib/Sema/SemaExprObjC.cpp | 8 +++----- test/Analysis/inlining/DynDispatchBifurcate.m | 2 +- test/Analysis/rdar-6540084.m | 4 ++-- test/Modules/objc-categories.m | 2 +- test/PCH/chain-categories2.m | 2 +- test/SemaObjC/call-super-2.m | 2 +- test/SemaObjC/compare-qualified-id.m | 2 +- test/SemaObjC/conditional-expr.m | 2 +- test/SemaObjC/error-outof-scope-property-use.m | 2 +- test/SemaObjC/instancetype.m | 2 +- test/SemaObjC/message.m | 2 +- test/SemaObjC/method-not-defined.m | 2 +- test/SemaObjC/missing-atend-metadata.m | 2 +- test/SemaObjC/property-5.m | 2 +- test/SemaObjC/protocol-id-test-1.m | 2 +- test/SemaObjCXX/instancetype.mm | 2 +- 17 files changed, 20 insertions(+), 22 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index bac681e871..55f237736c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -500,7 +500,7 @@ def note_while_in_implementation : Note< def note_class_declared : Note< "class is declared here">; def note_receiver_class_declared : Note< - "receiver is object of the class that is declared here">; + "receiver is instance of class declared here">; def note_receiver_is_id : Note< "receiver is treated with 'id' type for purpose of method lookup">; def note_suppressed_class_declare : Note< diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 7c701ea040..23116d84ad 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -1227,11 +1227,9 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, SelectorLocs.back()); // Find the class to which we are sending this message. if (ReceiverType->isObjCObjectPointerType()) { - QualType ClassType = - ReceiverType->getAs()->getPointeeType(); - if (const ObjCObjectType *ClassTPtr = ClassType->getAs()) - if (ObjCInterfaceDecl *Class = ClassTPtr->getInterface()) - Diag(Class->getLocation(), diag::note_receiver_class_declared); + if (ObjCInterfaceDecl *Class = + ReceiverType->getAs()->getInterfaceDecl()) + Diag(Class->getLocation(), diag::note_receiver_class_declared); } } diff --git a/test/Analysis/inlining/DynDispatchBifurcate.m b/test/Analysis/inlining/DynDispatchBifurcate.m index d8af213a06..0ce079654e 100644 --- a/test/Analysis/inlining/DynDispatchBifurcate.m +++ b/test/Analysis/inlining/DynDispatchBifurcate.m @@ -181,7 +181,7 @@ int testPropertySynthesized(PublicClass *p) { } // Test definition not available edge case. -@interface DefNotAvailClass : NSObject // expected-note {{receiver is object of the class that is declared here}} +@interface DefNotAvailClass : NSObject // expected-note {{receiver is instance of class declared here}} @end id testDefNotAvailableInlined(DefNotAvailClass *C) { return [C mem]; // expected-warning {{instance method '-mem' not found}} diff --git a/test/Analysis/rdar-6540084.m b/test/Analysis/rdar-6540084.m index d93450b5c5..e928710b38 100644 --- a/test/Analysis/rdar-6540084.m +++ b/test/Analysis/rdar-6540084.m @@ -9,13 +9,13 @@ typedef struct _NSZone NSZone; @protocol NSObject - (BOOL)isEqual:(id)object; @end @interface NSObject {} @end extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone); -@class NSArray; // expected-note {{receiver is object of the class that is declared here}} +@class NSArray; // expected-note {{receiver is instance of class declared here}} @class NSMutableArray, NSIndexSet, NSView, NSPredicate, NSString, NSViewAnimation, NSTimer; // expected-note{{forward declaration of class here}} @interface FooBazController : NSObject {} @end typedef struct {} TazVersion; @class TazNode; -@interface TazGuttenberg : NSObject {} typedef NSUInteger BugsBunnyType; @end // expected-note {{receiver is object of the class that is declared here}} +@interface TazGuttenberg : NSObject {} typedef NSUInteger BugsBunnyType; @end // expected-note {{receiver is instance of class declared here}} @interface FooBaz : NSObject {} @property (nonatomic) BugsBunnyType matchType; @property (nonatomic, retain) NSArray *papyrus; @end diff --git a/test/Modules/objc-categories.m b/test/Modules/objc-categories.m index 07d1760980..f08b13a78a 100644 --- a/test/Modules/objc-categories.m +++ b/test/Modules/objc-categories.m @@ -10,7 +10,7 @@ // expected-note@Inputs/category_left.h:14 {{previous definition}} // expected-warning@Inputs/category_right.h:11 {{duplicate definition of category}} -// expected-note@Inputs/category_top.h:1 {{receiver is object of the class that is declared here}} +// expected-note@Inputs/category_top.h:1 {{receiver is instance of class declared here}} @interface Foo(Source) -(void)source; diff --git a/test/PCH/chain-categories2.m b/test/PCH/chain-categories2.m index 8cb06a857c..50eea2a560 100644 --- a/test/PCH/chain-categories2.m +++ b/test/PCH/chain-categories2.m @@ -45,7 +45,7 @@ #else //===----------------------------------------------------------------------===// -// expected-note@30 {{receiver is object of the class that is declared here}} +// expected-note@30 {{receiver is instance of class declared here}} void f(I* i) { [i meth]; // expected-warning {{not found}} } diff --git a/test/SemaObjC/call-super-2.m b/test/SemaObjC/call-super-2.m index 18a5f09407..25cfbdd087 100644 --- a/test/SemaObjC/call-super-2.m +++ b/test/SemaObjC/call-super-2.m @@ -14,7 +14,7 @@ id objc_getClass(const char *s); - (int) instance_func0; @end -@interface Derived: Object // expected-note {{receiver is object of the class that is declared here}} +@interface Derived: Object // expected-note {{receiver is instance of class declared here}} + (int) class_func1; + (int) class_func2; + (int) class_func3; diff --git a/test/SemaObjC/compare-qualified-id.m b/test/SemaObjC/compare-qualified-id.m index 0b7e106b35..02fa86ec8d 100644 --- a/test/SemaObjC/compare-qualified-id.m +++ b/test/SemaObjC/compare-qualified-id.m @@ -12,7 +12,7 @@ typedef struct _NSZone NSZone; typedef struct {} NSFastEnumerationState; @protocol NSFastEnumeration - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len; @end @interface NSDictionary : NSObject - (NSUInteger)count; @end -@interface NSMutableDictionary : NSDictionary - (void)removeObjectForKey:(id)aKey; @end // expected-note {{receiver is object of the class that is declared here}} +@interface NSMutableDictionary : NSDictionary - (void)removeObjectForKey:(id)aKey; @end // expected-note {{receiver is instance of class declared here}} extern NSString * const NSTaskDidTerminateNotification; @interface XCPropertyExpansionContext : NSObject { // expected-note {{required for direct or indirect protocol 'NSCopying'}} diff --git a/test/SemaObjC/conditional-expr.m b/test/SemaObjC/conditional-expr.m index 083453810c..049a095ce3 100644 --- a/test/SemaObjC/conditional-expr.m +++ b/test/SemaObjC/conditional-expr.m @@ -34,7 +34,7 @@ // No @interface declaration for DTFilterOutputStream3 @implementation DTFilterOutputStream3 // expected-warning {{cannot find interface declaration for 'DTFilterOutputStream3'}} \ - // expected-note {{receiver is object of the class that is declared here}} + // expected-note {{receiver is instance of class declared here}} - (id)initWithNextOutputStream:(id ) outputStream { id nextOutputStream = [self nextOutputStream]; // expected-warning {{method '-nextOutputStream' not found (return type defaults to 'id')}} self = nextOutputStream; // expected-warning {{assigning to 'DTFilterOutputStream3 *' from incompatible type 'id'}} diff --git a/test/SemaObjC/error-outof-scope-property-use.m b/test/SemaObjC/error-outof-scope-property-use.m index 023687bbf4..8ab9f55745 100644 --- a/test/SemaObjC/error-outof-scope-property-use.m +++ b/test/SemaObjC/error-outof-scope-property-use.m @@ -2,7 +2,7 @@ // RUN: %clang_cc1 -x objective-c++ -fsyntax-only -fobjc-default-synthesize-properties -verify -Wno-objc-root-class %s // rdar://13178483 -@class NSMutableDictionary; // expected-note {{receiver is object of the class that is declared here}} +@class NSMutableDictionary; // expected-note {{receiver is instance of class declared here}} @interface LaunchdJobs diff --git a/test/SemaObjC/instancetype.m b/test/SemaObjC/instancetype.m index 2374890271..7811d3eba5 100644 --- a/test/SemaObjC/instancetype.m +++ b/test/SemaObjC/instancetype.m @@ -25,7 +25,7 @@ - (instancetype)otherMethodInProto2; // expected-note{{overridden method returns an instance of its class type}} @end -@interface Subclass1 : Root // expected-note 4 {{receiver is object of the class that is declared here}} +@interface Subclass1 : Root // expected-note 4 {{receiver is instance of class declared here}} - (instancetype)initSubclass1; - (void)methodOnSubclass1; + (instancetype)allocSubclass1; diff --git a/test/SemaObjC/message.m b/test/SemaObjC/message.m index fc75cdbf6e..2c4d8066f6 100644 --- a/test/SemaObjC/message.m +++ b/test/SemaObjC/message.m @@ -107,7 +107,7 @@ void foo5(id p) { // expected-warning {{instance method '-bar' not found}} } -@interface I1 // expected-note {{receiver is object of the class that is declared here}} +@interface I1 // expected-note {{receiver is instance of class declared here}} -(void)unavail_meth __attribute__((unavailable)); // expected-note {{marked unavailable here}} @end diff --git a/test/SemaObjC/method-not-defined.m b/test/SemaObjC/method-not-defined.m index 8f43c5daa7..792469b719 100644 --- a/test/SemaObjC/method-not-defined.m +++ b/test/SemaObjC/method-not-defined.m @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -@interface Foo // expected-note {{receiver is object of the class that is declared here}} +@interface Foo // expected-note {{receiver is instance of class declared here}} @end void test() { diff --git a/test/SemaObjC/missing-atend-metadata.m b/test/SemaObjC/missing-atend-metadata.m index 9d0024ac65..f235ab94d8 100644 --- a/test/SemaObjC/missing-atend-metadata.m +++ b/test/SemaObjC/missing-atend-metadata.m @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -triple i386-apple-darwin9 -fsyntax-only -verify -Wno-objc-root-class %s -@interface I0 // expected-note {{receiver is object of the class that is declared here}} +@interface I0 // expected-note {{receiver is instance of class declared here}} @end @implementation I0 // expected-note {{implementation started here}} diff --git a/test/SemaObjC/property-5.m b/test/SemaObjC/property-5.m index 782f77cfea..cf4f87392b 100644 --- a/test/SemaObjC/property-5.m +++ b/test/SemaObjC/property-5.m @@ -8,7 +8,7 @@ @interface MutableNSData : NSData @end -@interface Base : NSData // expected-note {{receiver is object of the class that is declared here}} +@interface Base : NSData // expected-note {{receiver is instance of class declared here}} @property(readonly) id ref; @property(readonly) Base *p_base; @property(readonly) NSData *nsdata; diff --git a/test/SemaObjC/protocol-id-test-1.m b/test/SemaObjC/protocol-id-test-1.m index 013f291ad9..36e23ed9f2 100644 --- a/test/SemaObjC/protocol-id-test-1.m +++ b/test/SemaObjC/protocol-id-test-1.m @@ -7,7 +7,7 @@ @protocol P @end -@interface INTF

// expected-note {{receiver is object of the class that is declared here}} +@interface INTF

// expected-note {{receiver is instance of class declared here}} - (void)IMeth; @end diff --git a/test/SemaObjCXX/instancetype.mm b/test/SemaObjCXX/instancetype.mm index 87663b9716..89ff2b4b03 100644 --- a/test/SemaObjCXX/instancetype.mm +++ b/test/SemaObjCXX/instancetype.mm @@ -25,7 +25,7 @@ - (instancetype)otherMethodInProto2; // expected-note{{overridden method returns an instance of its class type}} @end -@interface Subclass1 : Root // expected-note 4 {{receiver is object of the class that is declared here}} +@interface Subclass1 : Root // expected-note 4 {{receiver is instance of class declared here}} - (instancetype)initSubclass1; - (void)methodOnSubclass1; + (instancetype)allocSubclass1; -- cgit v1.2.3 From cce70c7c5f0f4c1a41658fbed845f8b3a565c99c Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Wed, 15 May 2013 18:08:15 +0000 Subject: [analyzer] Put back DefaultBool's implicit conversion to bool. DefaultBool is basically just "bool with a default constructor", so it really should implicitly convert to bool. In fact, it should convert to bool&, so that it could be passed to functions that take bools by reference. This time, mark the operator bool& as implicit to promise that it's deliberate. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181908 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/StaticAnalyzer/Core/Checker.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h index d56f8e9c1d..219e99cfd4 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/include/clang/StaticAnalyzer/Core/Checker.h @@ -502,10 +502,14 @@ struct ImplicitNullDerefEvent { }; /// \brief A helper class which wraps a boolean value set to false by default. +/// +/// This class should behave exactly like 'bool' except that it doesn't need to +/// be explicitly initialized. struct DefaultBool { bool val; DefaultBool() : val(false) {} - LLVM_EXPLICIT operator bool() const { return val; } + /*implicit*/ operator bool&() { return val; } + /*implicit*/ operator const bool&() const { return val; } DefaultBool &operator=(bool b) { val = b; return *this; } }; -- cgit v1.2.3