summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/clang/lib/Sema/SemaLookup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Sema/SemaLookup.cpp')
-rw-r--r--contrib/llvm/tools/clang/lib/Sema/SemaLookup.cpp924
1 files changed, 707 insertions, 217 deletions
diff --git a/contrib/llvm/tools/clang/lib/Sema/SemaLookup.cpp b/contrib/llvm/tools/clang/lib/Sema/SemaLookup.cpp
index 9ab3b2d..919c6ad 100644
--- a/contrib/llvm/tools/clang/lib/Sema/SemaLookup.cpp
+++ b/contrib/llvm/tools/clang/lib/Sema/SemaLookup.cpp
@@ -102,15 +102,14 @@ namespace {
// During unqualified name lookup, the names appear as if they
// were declared in the nearest enclosing namespace which contains
// both the using-directive and the nominated namespace.
- DeclContext *InnermostFileDC
- = static_cast<DeclContext*>(InnermostFileScope->getEntity());
+ DeclContext *InnermostFileDC = InnermostFileScope->getEntity();
assert(InnermostFileDC && InnermostFileDC->isFileContext());
for (; S; S = S->getParent()) {
// C++ [namespace.udir]p1:
// A using-directive shall not appear in class scope, but may
// appear in namespace scope or in block scope.
- DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity());
+ DeclContext *Ctx = S->getEntity();
if (Ctx && Ctx->isFileContext()) {
visit(Ctx, Ctx);
} else if (!Ctx || Ctx->isFunctionOrMethod()) {
@@ -167,8 +166,7 @@ namespace {
if (queue.empty())
return;
- DC = queue.back();
- queue.pop_back();
+ DC = queue.pop_back_val();
}
}
@@ -217,12 +215,15 @@ static inline unsigned getIDNS(Sema::LookupNameKind NameKind,
case Sema::LookupObjCImplicitSelfParam:
case Sema::LookupOrdinaryName:
case Sema::LookupRedeclarationWithLinkage:
+ case Sema::LookupLocalFriendName:
IDNS = Decl::IDNS_Ordinary;
if (CPlusPlus) {
IDNS |= Decl::IDNS_Tag | Decl::IDNS_Member | Decl::IDNS_Namespace;
if (Redeclaration)
IDNS |= Decl::IDNS_TagFriend | Decl::IDNS_OrdinaryFriend;
}
+ if (Redeclaration)
+ IDNS |= Decl::IDNS_LocalExtern;
break;
case Sema::LookupOperatorName:
@@ -336,10 +337,19 @@ void LookupResult::deletePaths(CXXBasePaths *Paths) {
delete Paths;
}
-static NamedDecl *getVisibleDecl(NamedDecl *D);
-
-NamedDecl *LookupResult::getAcceptableDeclSlow(NamedDecl *D) const {
- return getVisibleDecl(D);
+/// Get a representative context for a declaration such that two declarations
+/// will have the same context if they were found within the same scope.
+static DeclContext *getContextForScopeMatching(Decl *D) {
+ // For function-local declarations, use that function as the context. This
+ // doesn't account for scopes within the function; the caller must deal with
+ // those.
+ DeclContext *DC = D->getLexicalDeclContext();
+ if (DC->isFunctionOrMethod())
+ return DC;
+
+ // Otherwise, look at the semantic context of the declaration. The
+ // declaration must have been found there.
+ return D->getDeclContext()->getRedeclContext();
}
/// Resolves the result kind of this lookup.
@@ -442,8 +452,8 @@ void LookupResult::resolveKind() {
// even if they're not visible. (ref?)
if (HideTags && HasTag && !Ambiguous &&
(HasFunction || HasNonFunction || HasUnresolved)) {
- if (Decls[UniqueTagIndex]->getDeclContext()->getRedeclContext()->Equals(
- Decls[UniqueTagIndex? 0 : N-1]->getDeclContext()->getRedeclContext()))
+ if (getContextForScopeMatching(Decls[UniqueTagIndex])->Equals(
+ getContextForScopeMatching(Decls[UniqueTagIndex ? 0 : N - 1])))
Decls[UniqueTagIndex] = Decls[--N];
else
Ambiguous = true;
@@ -511,6 +521,14 @@ static bool LookupBuiltin(Sema &S, LookupResult &R) {
NameKind == Sema::LookupRedeclarationWithLinkage) {
IdentifierInfo *II = R.getLookupName().getAsIdentifierInfo();
if (II) {
+ if (S.getLangOpts().CPlusPlus11 && S.getLangOpts().GNUMode &&
+ II == S.getFloat128Identifier()) {
+ // libstdc++4.7's type_traits expects type __float128 to exist, so
+ // insert a dummy type to make that header build in gnu++11 mode.
+ R.addDecl(S.getASTContext().getFloat128StubType());
+ return true;
+ }
+
// If this is a builtin on this (or all) targets, create the decl.
if (unsigned BuiltinID = II->getBuiltinID()) {
// In C++, we don't have any predefined library functions like
@@ -726,7 +744,7 @@ static bool LookupDirect(Sema &S, LookupResult &R, const DeclContext *DC) {
// function to have, if it were to match the name given.
// FIXME: Calling convention!
FunctionProtoType::ExtProtoInfo EPI = ConvProto->getExtProtoInfo();
- EPI.ExtInfo = EPI.ExtInfo.withCallingConv(CC_Default);
+ EPI.ExtInfo = EPI.ExtInfo.withCallingConv(CC_C);
EPI.ExceptionSpecType = EST_None;
EPI.NumExceptions = 0;
QualType ExpectedType
@@ -771,7 +789,7 @@ CppNamespaceLookup(Sema &S, LookupResult &R, ASTContext &Context,
}
static bool isNamespaceOrTranslationUnitScope(Scope *S) {
- if (DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity()))
+ if (DeclContext *Ctx = S->getEntity())
return Ctx->isFileContext();
return false;
}
@@ -784,12 +802,12 @@ static bool isNamespaceOrTranslationUnitScope(Scope *S) {
// name lookup should continue searching in this semantic context when
// it leaves the current template parameter scope.
static std::pair<DeclContext *, bool> findOuterContext(Scope *S) {
- DeclContext *DC = static_cast<DeclContext *>(S->getEntity());
+ DeclContext *DC = S->getEntity();
DeclContext *Lexical = 0;
for (Scope *OuterS = S->getParent(); OuterS;
OuterS = OuterS->getParent()) {
if (OuterS->getEntity()) {
- Lexical = static_cast<DeclContext *>(OuterS->getEntity());
+ Lexical = OuterS->getEntity();
break;
}
}
@@ -845,16 +863,37 @@ static std::pair<DeclContext *, bool> findOuterContext(Scope *S) {
return std::make_pair(Lexical, false);
}
+namespace {
+/// An RAII object to specify that we want to find block scope extern
+/// declarations.
+struct FindLocalExternScope {
+ FindLocalExternScope(LookupResult &R)
+ : R(R), OldFindLocalExtern(R.getIdentifierNamespace() &
+ Decl::IDNS_LocalExtern) {
+ R.setFindLocalExtern(R.getIdentifierNamespace() & Decl::IDNS_Ordinary);
+ }
+ void restore() {
+ R.setFindLocalExtern(OldFindLocalExtern);
+ }
+ ~FindLocalExternScope() {
+ restore();
+ }
+ LookupResult &R;
+ bool OldFindLocalExtern;
+};
+}
+
bool Sema::CppLookupName(LookupResult &R, Scope *S) {
assert(getLangOpts().CPlusPlus && "Can perform only C++ lookup");
DeclarationName Name = R.getLookupName();
+ Sema::LookupNameKind NameKind = R.getLookupKind();
// If this is the name of an implicitly-declared special member function,
// go through the scope stack to implicitly declare
if (isImplicitlyDeclaredMemberFunctionName(Name)) {
for (Scope *PreS = S; PreS; PreS = PreS->getParent())
- if (DeclContext *DC = static_cast<DeclContext *>(PreS->getEntity()))
+ if (DeclContext *DC = PreS->getEntity())
DeclareImplicitMemberFunctionsWithName(*this, Name, DC);
}
@@ -886,14 +925,35 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
//
UnqualUsingDirectiveSet UDirs;
bool VisitedUsingDirectives = false;
+ bool LeftStartingScope = false;
DeclContext *OutsideOfTemplateParamDC = 0;
+
+ // When performing a scope lookup, we want to find local extern decls.
+ FindLocalExternScope FindLocals(R);
+
for (; S && !isNamespaceOrTranslationUnitScope(S); S = S->getParent()) {
- DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity());
+ DeclContext *Ctx = S->getEntity();
// Check whether the IdResolver has anything in this scope.
bool Found = false;
for (; I != IEnd && S->isDeclScope(*I); ++I) {
if (NamedDecl *ND = R.getAcceptableDecl(*I)) {
+ if (NameKind == LookupRedeclarationWithLinkage) {
+ // Determine whether this (or a previous) declaration is
+ // out-of-scope.
+ if (!LeftStartingScope && !Initial->isDeclScope(*I))
+ LeftStartingScope = true;
+
+ // If we found something outside of our starting scope that
+ // does not have linkage, skip it. If it's a template parameter,
+ // we still find it, so we can diagnose the invalid redeclaration.
+ if (LeftStartingScope && !((*I)->hasLinkage()) &&
+ !(*I)->isTemplateParameter()) {
+ R.setShadowed();
+ continue;
+ }
+ }
+
Found = true;
R.addDecl(ND);
}
@@ -906,6 +966,15 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
return true;
}
+ if (NameKind == LookupLocalFriendName && !S->isClassScope()) {
+ // C++11 [class.friend]p11:
+ // If a friend declaration appears in a local class and the name
+ // specified is an unqualified name, a prior declaration is
+ // looked up without considering scopes that are outside the
+ // innermost enclosing non-class scope.
+ return false;
+ }
+
if (!Ctx && S->isTemplateParamScope() && OutsideOfTemplateParamDC &&
S->getParent() && !S->getParent()->isTemplateParamScope()) {
// We've just searched the last template parameter scope and
@@ -1007,7 +1076,7 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
if (!S) return false;
// If we are looking for members, no need to look into global/namespace scope.
- if (R.getLookupKind() == LookupMemberName)
+ if (NameKind == LookupMemberName)
return false;
// Collect UsingDirectiveDecls in all scopes, and recursively all
@@ -1019,7 +1088,12 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
UDirs.visitScopeChain(Initial, S);
UDirs.done();
}
-
+
+ // If we're not performing redeclaration lookup, do not look for local
+ // extern declarations outside of a function scope.
+ if (!R.isForRedeclaration())
+ FindLocals.restore();
+
// Lookup namespace scope, and global scope.
// Unqualified name lookup in C++ requires looking into scopes
// that aren't strictly lexical, and therefore we walk through the
@@ -1043,7 +1117,7 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
return true;
}
- DeclContext *Ctx = static_cast<DeclContext *>(S->getEntity());
+ DeclContext *Ctx = S->getEntity();
if (!Ctx && S->isTemplateParamScope() && OutsideOfTemplateParamDC &&
S->getParent() && !S->getParent()->isTemplateParamScope()) {
// We've just searched the last template parameter scope and
@@ -1098,29 +1172,127 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
return !R.empty();
}
+/// \brief Find the declaration that a class temploid member specialization was
+/// instantiated from, or the member itself if it is an explicit specialization.
+static Decl *getInstantiatedFrom(Decl *D, MemberSpecializationInfo *MSInfo) {
+ return MSInfo->isExplicitSpecialization() ? D : MSInfo->getInstantiatedFrom();
+}
+
+/// \brief Find the module in which the given declaration was defined.
+static Module *getDefiningModule(Decl *Entity) {
+ if (FunctionDecl *FD = dyn_cast<FunctionDecl>(Entity)) {
+ // If this function was instantiated from a template, the defining module is
+ // the module containing the pattern.
+ if (FunctionDecl *Pattern = FD->getTemplateInstantiationPattern())
+ Entity = Pattern;
+ } else if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Entity)) {
+ // If it's a class template specialization, find the template or partial
+ // specialization from which it was instantiated.
+ if (ClassTemplateSpecializationDecl *SpecRD =
+ dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+ llvm::PointerUnion<ClassTemplateDecl*,
+ ClassTemplatePartialSpecializationDecl*> From =
+ SpecRD->getInstantiatedFrom();
+ if (ClassTemplateDecl *FromTemplate = From.dyn_cast<ClassTemplateDecl*>())
+ Entity = FromTemplate->getTemplatedDecl();
+ else if (From)
+ Entity = From.get<ClassTemplatePartialSpecializationDecl*>();
+ // Otherwise, it's an explicit specialization.
+ } else if (MemberSpecializationInfo *MSInfo =
+ RD->getMemberSpecializationInfo())
+ Entity = getInstantiatedFrom(RD, MSInfo);
+ } else if (EnumDecl *ED = dyn_cast<EnumDecl>(Entity)) {
+ if (MemberSpecializationInfo *MSInfo = ED->getMemberSpecializationInfo())
+ Entity = getInstantiatedFrom(ED, MSInfo);
+ } else if (VarDecl *VD = dyn_cast<VarDecl>(Entity)) {
+ // FIXME: Map from variable template specializations back to the template.
+ if (MemberSpecializationInfo *MSInfo = VD->getMemberSpecializationInfo())
+ Entity = getInstantiatedFrom(VD, MSInfo);
+ }
+
+ // Walk up to the containing context. That might also have been instantiated
+ // from a template.
+ DeclContext *Context = Entity->getDeclContext();
+ if (Context->isFileContext())
+ return Entity->getOwningModule();
+ return getDefiningModule(cast<Decl>(Context));
+}
+
+llvm::DenseSet<Module*> &Sema::getLookupModules() {
+ unsigned N = ActiveTemplateInstantiations.size();
+ for (unsigned I = ActiveTemplateInstantiationLookupModules.size();
+ I != N; ++I) {
+ Module *M = getDefiningModule(ActiveTemplateInstantiations[I].Entity);
+ if (M && !LookupModulesCache.insert(M).second)
+ M = 0;
+ ActiveTemplateInstantiationLookupModules.push_back(M);
+ }
+ return LookupModulesCache;
+}
+
+/// \brief Determine whether a declaration is visible to name lookup.
+///
+/// This routine determines whether the declaration D is visible in the current
+/// lookup context, taking into account the current template instantiation
+/// stack. During template instantiation, a declaration is visible if it is
+/// visible from a module containing any entity on the template instantiation
+/// path (by instantiating a template, you allow it to see the declarations that
+/// your module can see, including those later on in your module).
+bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) {
+ assert(D->isHidden() && !SemaRef.ActiveTemplateInstantiations.empty() &&
+ "should not call this: not in slow case");
+ Module *DeclModule = D->getOwningModule();
+ assert(DeclModule && "hidden decl not from a module");
+
+ // Find the extra places where we need to look.
+ llvm::DenseSet<Module*> &LookupModules = SemaRef.getLookupModules();
+ if (LookupModules.empty())
+ return false;
+
+ // If our lookup set contains the decl's module, it's visible.
+ if (LookupModules.count(DeclModule))
+ return true;
+
+ // If the declaration isn't exported, it's not visible in any other module.
+ if (D->isModulePrivate())
+ return false;
+
+ // Check whether DeclModule is transitively exported to an import of
+ // the lookup set.
+ for (llvm::DenseSet<Module *>::iterator I = LookupModules.begin(),
+ E = LookupModules.end();
+ I != E; ++I)
+ if ((*I)->isModuleVisible(DeclModule))
+ return true;
+ return false;
+}
+
/// \brief Retrieve the visible declaration corresponding to D, if any.
///
/// This routine determines whether the declaration D is visible in the current
/// module, with the current imports. If not, it checks whether any
/// redeclaration of D is visible, and if so, returns that declaration.
-///
+///
/// \returns D, or a visible previous declaration of D, whichever is more recent
/// and visible. If no declaration of D is visible, returns null.
-static NamedDecl *getVisibleDecl(NamedDecl *D) {
- if (LookupResult::isVisible(D))
- return D;
-
+static NamedDecl *findAcceptableDecl(Sema &SemaRef, NamedDecl *D) {
+ assert(!LookupResult::isVisible(SemaRef, D) && "not in slow case");
+
for (Decl::redecl_iterator RD = D->redecls_begin(), RDEnd = D->redecls_end();
RD != RDEnd; ++RD) {
if (NamedDecl *ND = dyn_cast<NamedDecl>(*RD)) {
- if (LookupResult::isVisible(ND))
+ if (LookupResult::isVisible(SemaRef, ND))
return ND;
}
}
-
+
return 0;
}
+NamedDecl *LookupResult::getAcceptableDeclSlow(NamedDecl *D) const {
+ return findAcceptableDecl(SemaRef, D);
+}
+
/// @brief Perform unqualified name lookup starting from a given
/// scope.
///
@@ -1161,13 +1333,12 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) {
if (NameKind == Sema::LookupRedeclarationWithLinkage) {
// Find the nearest non-transparent declaration scope.
while (!(S->getFlags() & Scope::DeclScope) ||
- (S->getEntity() &&
- static_cast<DeclContext *>(S->getEntity())
- ->isTransparentContext()))
+ (S->getEntity() && S->getEntity()->isTransparentContext()))
S = S->getParent();
}
- unsigned IDNS = R.getIdentifierNamespace();
+ // When performing a scope lookup, we want to find local extern decls.
+ FindLocalExternScope FindLocals(R);
// Scan up the scope chain looking for a decl that matches this
// identifier that is in the appropriate namespace. This search
@@ -1178,7 +1349,7 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) {
for (IdentifierResolver::iterator I = IdResolver.begin(Name),
IEnd = IdResolver.end();
I != IEnd; ++I)
- if ((*I)->isInIdentifierNamespace(IDNS)) {
+ if (NamedDecl *D = R.getAcceptableDecl(*I)) {
if (NameKind == LookupRedeclarationWithLinkage) {
// Determine whether this (or a previous) declaration is
// out-of-scope.
@@ -1187,19 +1358,15 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) {
// If we found something outside of our starting scope that
// does not have linkage, skip it.
- if (LeftStartingScope && !((*I)->hasLinkage()))
+ if (LeftStartingScope && !((*I)->hasLinkage())) {
+ R.setShadowed();
continue;
+ }
}
else if (NameKind == LookupObjCImplicitSelfParam &&
!isa<ImplicitParamDecl>(*I))
continue;
-
- // If this declaration is module-private and it came from an AST
- // file, we can't see it.
- NamedDecl *D = R.isHiddenDeclarationVisible()? *I : getVisibleDecl(*I);
- if (!D)
- continue;
-
+
R.addDecl(D);
// Check whether there are any other declarations with the same name
@@ -1234,18 +1401,15 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) {
if (!LastDC->Equals(DC))
break;
}
-
- // If the declaration isn't in the right namespace, skip it.
- if (!(*LastI)->isInIdentifierNamespace(IDNS))
- continue;
-
- D = R.isHiddenDeclarationVisible()? *LastI : getVisibleDecl(*LastI);
- if (D)
- R.addDecl(D);
+
+ // If the declaration is in the right namespace and visible, add it.
+ if (NamedDecl *LastD = R.getAcceptableDecl(*LastI))
+ R.addDecl(LastD);
}
R.resolveKind();
}
+
return true;
}
} else {
@@ -1330,8 +1494,7 @@ static bool LookupQualifiedNameInUsingDirectives(Sema &S, LookupResult &R,
bool Found = false;
while (!Queue.empty()) {
- NamespaceDecl *ND = Queue.back();
- Queue.pop_back();
+ NamespaceDecl *ND = Queue.pop_back_val();
// We go through some convolutions here to avoid copying results
// between LookupResults.
@@ -1510,6 +1673,7 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
case LookupOrdinaryName:
case LookupMemberName:
case LookupRedeclarationWithLinkage:
+ case LookupLocalFriendName:
BaseCallback = &CXXRecordDecl::FindOrdinaryMember;
break;
@@ -1681,9 +1845,7 @@ bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS,
/// from name lookup.
///
/// \param Result The result of the ambiguous lookup to be diagnosed.
-///
-/// \returns true
-bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result) {
+void Sema::DiagnoseAmbiguousLookup(LookupResult &Result) {
assert(Result.isAmbiguous() && "Lookup result must be ambiguous");
DeclarationName Name = Result.getLookupName();
@@ -1704,8 +1866,7 @@ bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result) {
++Found;
Diag((*Found)->getLocation(), diag::note_ambiguous_member_found);
-
- return true;
+ break;
}
case LookupResult::AmbiguousBaseSubobjectTypes: {
@@ -1721,8 +1882,7 @@ bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result) {
if (DeclsPrinted.insert(D).second)
Diag(D->getLocation(), diag::note_ambiguous_member_found);
}
-
- return true;
+ break;
}
case LookupResult::AmbiguousTagHiding: {
@@ -1748,8 +1908,7 @@ bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result) {
F.erase();
}
F.done();
-
- return true;
+ break;
}
case LookupResult::AmbiguousReference: {
@@ -1758,12 +1917,9 @@ bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result) {
LookupResult::iterator DI = Result.begin(), DE = Result.end();
for (; DI != DE; ++DI)
Diag((*DI)->getLocation(), diag::note_ambiguous_candidate) << *DI;
-
- return true;
+ break;
}
}
-
- llvm_unreachable("unknown ambiguity kind");
}
namespace {
@@ -1922,8 +2078,7 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result,
Bases.push_back(Class);
while (!Bases.empty()) {
// Pop this class off the stack.
- Class = Bases.back();
- Bases.pop_back();
+ Class = Bases.pop_back_val();
// Visit the base classes.
for (CXXRecordDecl::base_class_iterator Base = Class->bases_begin(),
@@ -2107,9 +2262,9 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) {
continue;
}
- if (Queue.empty()) break;
- T = Queue.back();
- Queue.pop_back();
+ if (Queue.empty())
+ break;
+ T = Queue.pop_back_val();
}
}
@@ -2120,11 +2275,10 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) {
/// This routine computes the sets of associated classes and associated
/// namespaces searched by argument-dependent lookup
/// (C++ [basic.lookup.argdep]) for a given set of arguments.
-void
-Sema::FindAssociatedClassesAndNamespaces(SourceLocation InstantiationLoc,
- llvm::ArrayRef<Expr *> Args,
- AssociatedNamespaceSet &AssociatedNamespaces,
- AssociatedClassSet &AssociatedClasses) {
+void Sema::FindAssociatedClassesAndNamespaces(
+ SourceLocation InstantiationLoc, ArrayRef<Expr *> Args,
+ AssociatedNamespaceSet &AssociatedNamespaces,
+ AssociatedClassSet &AssociatedClasses) {
AssociatedNamespaces.clear();
AssociatedClasses.clear();
@@ -2393,11 +2547,17 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD,
// will always be a (possibly implicit) declaration to shadow any others.
OverloadCandidateSet OCS((SourceLocation()));
DeclContext::lookup_result R = RD->lookup(Name);
-
assert(!R.empty() &&
"lookup for a constructor or assignment operator was empty");
- for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) {
- Decl *Cand = *I;
+
+ // Copy the candidates as our processing of them may load new declarations
+ // from an external source and invalidate lookup_result.
+ SmallVector<NamedDecl *, 8> Candidates(R.begin(), R.end());
+
+ for (SmallVectorImpl<NamedDecl *>::iterator I = Candidates.begin(),
+ E = Candidates.end();
+ I != E; ++I) {
+ NamedDecl *Cand = *I;
if (Cand->isInvalidDecl())
continue;
@@ -2565,7 +2725,8 @@ CXXDestructorDecl *Sema::LookupDestructor(CXXRecordDecl *Class) {
Sema::LiteralOperatorLookupResult
Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
ArrayRef<QualType> ArgTys,
- bool AllowRawAndTemplate) {
+ bool AllowRaw, bool AllowTemplate,
+ bool AllowStringTemplate) {
LookupName(R, S);
assert(R.getResultKind() != LookupResult::Ambiguous &&
"literal operator lookup can't be ambiguous");
@@ -2573,8 +2734,9 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
// Filter the lookup results appropriately.
LookupResult::Filter F = R.makeFilter();
- bool FoundTemplate = false;
bool FoundRaw = false;
+ bool FoundTemplate = false;
+ bool FoundStringTemplate = false;
bool FoundExactMatch = false;
while (F.hasNext()) {
@@ -2582,16 +2744,17 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
if (UsingShadowDecl *USD = dyn_cast<UsingShadowDecl>(D))
D = USD->getTargetDecl();
- bool IsTemplate = isa<FunctionTemplateDecl>(D);
- bool IsRaw = false;
- bool IsExactMatch = false;
-
// If the declaration we found is invalid, skip it.
if (D->isInvalidDecl()) {
F.erase();
continue;
}
+ bool IsRaw = false;
+ bool IsTemplate = false;
+ bool IsStringTemplate = false;
+ bool IsExactMatch = false;
+
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->getNumParams() == 1 &&
FD->getParamDecl(0)->getType()->getAs<PointerType>())
@@ -2607,19 +2770,31 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
}
}
}
+ if (FunctionTemplateDecl *FD = dyn_cast<FunctionTemplateDecl>(D)) {
+ TemplateParameterList *Params = FD->getTemplateParameters();
+ if (Params->size() == 1)
+ IsTemplate = true;
+ else
+ IsStringTemplate = true;
+ }
if (IsExactMatch) {
FoundExactMatch = true;
- AllowRawAndTemplate = false;
- if (FoundRaw || FoundTemplate) {
+ AllowRaw = false;
+ AllowTemplate = false;
+ AllowStringTemplate = false;
+ if (FoundRaw || FoundTemplate || FoundStringTemplate) {
// Go through again and remove the raw and template decls we've
// already found.
F.restart();
- FoundRaw = FoundTemplate = false;
+ FoundRaw = FoundTemplate = FoundStringTemplate = false;
}
- } else if (AllowRawAndTemplate && (IsTemplate || IsRaw)) {
- FoundTemplate |= IsTemplate;
- FoundRaw |= IsRaw;
+ } else if (AllowRaw && IsRaw) {
+ FoundRaw = true;
+ } else if (AllowTemplate && IsTemplate) {
+ FoundTemplate = true;
+ } else if (AllowStringTemplate && IsStringTemplate) {
+ FoundStringTemplate = true;
} else {
F.erase();
}
@@ -2654,10 +2829,14 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
if (FoundTemplate)
return LOLR_Template;
+ if (FoundStringTemplate)
+ return LOLR_StringTemplate;
+
// Didn't find anything we could use.
Diag(R.getNameLoc(), diag::err_ovl_no_viable_literal_operator)
<< R.getLookupName() << (int)ArgTys.size() << ArgTys[0]
- << (ArgTys.size() == 2 ? ArgTys[1] : QualType()) << AllowRawAndTemplate;
+ << (ArgTys.size() == 2 ? ArgTys[1] : QualType()) << AllowRaw
+ << (AllowTemplate || AllowStringTemplate);
return LOLR_Error;
}
@@ -2699,8 +2878,7 @@ void ADLResult::insert(NamedDecl *New) {
}
void Sema::ArgumentDependentLookup(DeclarationName Name, bool Operator,
- SourceLocation Loc,
- llvm::ArrayRef<Expr *> Args,
+ SourceLocation Loc, ArrayRef<Expr *> Args,
ADLResult &Result) {
// Find all of the associated namespaces and classes based on the
// arguments we have.
@@ -2748,9 +2926,21 @@ void Sema::ArgumentDependentLookup(DeclarationName Name, bool Operator,
NamedDecl *D = *I;
// If the only declaration here is an ordinary friend, consider
// it only if it was declared in an associated classes.
- if (D->getIdentifierNamespace() == Decl::IDNS_OrdinaryFriend) {
- DeclContext *LexDC = D->getLexicalDeclContext();
- if (!AssociatedClasses.count(cast<CXXRecordDecl>(LexDC)))
+ if ((D->getIdentifierNamespace() & Decl::IDNS_Ordinary) == 0) {
+ // If it's neither ordinarily visible nor a friend, we can't find it.
+ if ((D->getIdentifierNamespace() & Decl::IDNS_OrdinaryFriend) == 0)
+ continue;
+
+ bool DeclaredInAssociatedClass = false;
+ for (Decl *DI = D; DI; DI = DI->getPreviousDecl()) {
+ DeclContext *LexDC = DI->getLexicalDeclContext();
+ if (isa<CXXRecordDecl>(LexDC) &&
+ AssociatedClasses.count(cast<CXXRecordDecl>(LexDC))) {
+ DeclaredInAssociatedClass = true;
+ break;
+ }
+ }
+ if (!DeclaredInAssociatedClass)
continue;
}
@@ -2775,6 +2965,8 @@ void Sema::ArgumentDependentLookup(DeclarationName Name, bool Operator,
//----------------------------------------------------------------------------
VisibleDeclConsumer::~VisibleDeclConsumer() { }
+bool VisibleDeclConsumer::includeHiddenDecls() const { return false; }
+
namespace {
class ShadowContextRAII;
@@ -3038,8 +3230,9 @@ static void LookupVisibleDecls(Scope *S, LookupResult &Result,
if (!S->getEntity() ||
(!S->getParent() &&
- !Visited.alreadyVisitedContext((DeclContext *)S->getEntity())) ||
- ((DeclContext *)S->getEntity())->isFunctionOrMethod()) {
+ !Visited.alreadyVisitedContext(S->getEntity())) ||
+ (S->getEntity())->isFunctionOrMethod()) {
+ FindLocalExternScope FindLocals(Result);
// Walk through the declarations in this Scope.
for (Scope::decl_iterator D = S->decl_begin(), DEnd = S->decl_end();
D != DEnd; ++D) {
@@ -3057,7 +3250,7 @@ static void LookupVisibleDecls(Scope *S, LookupResult &Result,
// Look into this scope's declaration context, along with any of its
// parent lookup contexts (e.g., enclosing classes), up to the point
// where we hit the context stored in the next outer scope.
- Entity = (DeclContext *)S->getEntity();
+ Entity = S->getEntity();
DeclContext *OuterCtx = findOuterContext(S).first; // FIXME
for (DeclContext *Ctx = Entity; Ctx && !Ctx->Equals(OuterCtx);
@@ -3069,7 +3262,7 @@ static void LookupVisibleDecls(Scope *S, LookupResult &Result,
Result.getNameLoc(), Sema::LookupMemberName);
if (ObjCInterfaceDecl *IFace = Method->getClassInterface()) {
LookupVisibleDecls(IFace, IvarResult, /*QualifiedNameLookup=*/false,
- /*InBaseClass=*/false, Consumer, Visited);
+ /*InBaseClass=*/false, Consumer, Visited);
}
}
@@ -3134,6 +3327,7 @@ void Sema::LookupVisibleDecls(Scope *S, LookupNameKind Kind,
// Look for visible declarations.
LookupResult Result(*this, DeclarationName(), SourceLocation(), Kind);
+ Result.setAllowHidden(Consumer.includeHiddenDecls());
VisibleDeclsRecord Visited;
if (!IncludeGlobalScope)
Visited.visitedContext(Context.getTranslationUnitDecl());
@@ -3145,6 +3339,7 @@ void Sema::LookupVisibleDecls(DeclContext *Ctx, LookupNameKind Kind,
VisibleDeclConsumer &Consumer,
bool IncludeGlobalScope) {
LookupResult Result(*this, DeclarationName(), SourceLocation(), Kind);
+ Result.setAllowHidden(Consumer.includeHiddenDecls());
VisibleDeclsRecord Visited;
if (!IncludeGlobalScope)
Visited.visitedContext(Context.getTranslationUnitDecl());
@@ -3214,14 +3409,16 @@ class TypoCorrectionConsumer : public VisibleDeclConsumer {
public:
explicit TypoCorrectionConsumer(Sema &SemaRef, IdentifierInfo *Typo)
: Typo(Typo->getName()),
- SemaRef(SemaRef) { }
+ SemaRef(SemaRef) {}
+
+ bool includeHiddenDecls() const { return true; }
virtual void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx,
bool InBaseClass);
void FoundName(StringRef Name);
void addKeywordResult(StringRef Keyword);
- void addName(StringRef Name, NamedDecl *ND, unsigned Distance,
- NestedNameSpecifier *NNS=NULL, bool isKeyword=false);
+ void addName(StringRef Name, NamedDecl *ND, NestedNameSpecifier *NNS = NULL,
+ bool isKeyword = false);
void addCorrection(TypoCorrection Correction);
typedef TypoResultsMap::iterator result_iterator;
@@ -3265,37 +3462,42 @@ void TypoCorrectionConsumer::FoundDecl(NamedDecl *ND, NamedDecl *Hiding,
if (!Name)
return;
+ // Only consider visible declarations and declarations from modules with
+ // names that exactly match.
+ if (!LookupResult::isVisible(SemaRef, ND) && Name->getName() != Typo &&
+ !findAcceptableDecl(SemaRef, ND))
+ return;
+
FoundName(Name->getName());
}
void TypoCorrectionConsumer::FoundName(StringRef Name) {
- // Use a simple length-based heuristic to determine the minimum possible
- // edit distance. If the minimum isn't good enough, bail out early.
- unsigned MinED = abs((int)Name.size() - (int)Typo.size());
- if (MinED && Typo.size() / MinED < 3)
- return;
-
- // Compute an upper bound on the allowable edit distance, so that the
- // edit-distance algorithm can short-circuit.
- unsigned UpperBound = (Typo.size() + 2) / 3;
-
// Compute the edit distance between the typo and the name of this
// entity, and add the identifier to the list of results.
- addName(Name, NULL, Typo.edit_distance(Name, true, UpperBound));
+ addName(Name, NULL);
}
void TypoCorrectionConsumer::addKeywordResult(StringRef Keyword) {
// Compute the edit distance between the typo and this keyword,
// and add the keyword to the list of results.
- addName(Keyword, NULL, Typo.edit_distance(Keyword), NULL, true);
+ addName(Keyword, NULL, NULL, true);
}
-void TypoCorrectionConsumer::addName(StringRef Name,
- NamedDecl *ND,
- unsigned Distance,
- NestedNameSpecifier *NNS,
- bool isKeyword) {
- TypoCorrection TC(&SemaRef.Context.Idents.get(Name), ND, NNS, Distance);
+void TypoCorrectionConsumer::addName(StringRef Name, NamedDecl *ND,
+ NestedNameSpecifier *NNS, bool isKeyword) {
+ // Use a simple length-based heuristic to determine the minimum possible
+ // edit distance. If the minimum isn't good enough, bail out early.
+ unsigned MinED = abs((int)Name.size() - (int)Typo.size());
+ if (MinED && Typo.size() / MinED < 3)
+ return;
+
+ // Compute an upper bound on the allowable edit distance, so that the
+ // edit-distance algorithm can short-circuit.
+ unsigned UpperBound = (Typo.size() + 2) / 3 + 1;
+ unsigned ED = Typo.edit_distance(Name, true, UpperBound);
+ if (ED >= UpperBound) return;
+
+ TypoCorrection TC(&SemaRef.Context.Idents.get(Name), ND, NNS, ED);
if (isKeyword) TC.makeKeyword();
addCorrection(TC);
}
@@ -3388,6 +3590,7 @@ typedef SmallVector<SpecifierInfo, 16> SpecifierInfoList;
class NamespaceSpecifierSet {
ASTContext &Context;
DeclContextList CurContextChain;
+ std::string CurNameSpecifier;
SmallVector<const IdentifierInfo*, 4> CurContextIdentifiers;
SmallVector<const IdentifierInfo*, 4> CurNameSpecifierIdentifiers;
bool isSorted;
@@ -3406,10 +3609,14 @@ class NamespaceSpecifierSet {
NamespaceSpecifierSet(ASTContext &Context, DeclContext *CurContext,
CXXScopeSpec *CurScopeSpec)
: Context(Context), CurContextChain(BuildContextChain(CurContext)),
- isSorted(true) {
- if (CurScopeSpec && CurScopeSpec->getScopeRep())
- getNestedNameSpecifierIdentifiers(CurScopeSpec->getScopeRep(),
- CurNameSpecifierIdentifiers);
+ isSorted(false) {
+ if (NestedNameSpecifier *NNS =
+ CurScopeSpec ? CurScopeSpec->getScopeRep() : 0) {
+ llvm::raw_string_ostream SpecifierOStream(CurNameSpecifier);
+ NNS->print(SpecifierOStream, Context.getPrintingPolicy());
+
+ getNestedNameSpecifierIdentifiers(NNS, CurNameSpecifierIdentifiers);
+ }
// Build the list of identifiers that would be used for an absolute
// (from the global context) NestedNameSpecifier referring to the current
// context.
@@ -3419,11 +3626,17 @@ class NamespaceSpecifierSet {
if (NamespaceDecl *ND = dyn_cast_or_null<NamespaceDecl>(*C))
CurContextIdentifiers.push_back(ND->getIdentifier());
}
+
+ // Add the global context as a NestedNameSpecifier
+ Distances.insert(1);
+ DistanceMap[1].push_back(
+ SpecifierInfo(cast<DeclContext>(Context.getTranslationUnitDecl()),
+ NestedNameSpecifier::GlobalSpecifier(Context), 1));
}
- /// \brief Add the namespace to the set, computing the corresponding
- /// NestedNameSpecifier and its distance in the process.
- void AddNamespace(NamespaceDecl *ND);
+ /// \brief Add the DeclContext (a namespace or record) to the set, computing
+ /// the corresponding NestedNameSpecifier and its distance in the process.
+ void AddNameSpecifier(DeclContext *Ctx);
typedef SpecifierInfoList::iterator iterator;
iterator begin() {
@@ -3456,8 +3669,8 @@ void NamespaceSpecifierSet::SortNamespaces() {
std::sort(sortedDistances.begin(), sortedDistances.end());
Specifiers.clear();
- for (SmallVector<unsigned, 4>::iterator DI = sortedDistances.begin(),
- DIEnd = sortedDistances.end();
+ for (SmallVectorImpl<unsigned>::iterator DI = sortedDistances.begin(),
+ DIEnd = sortedDistances.end();
DI != DIEnd; ++DI) {
SpecifierInfoList &SpecList = DistanceMap[*DI];
Specifiers.append(SpecList.begin(), SpecList.end());
@@ -3466,8 +3679,26 @@ void NamespaceSpecifierSet::SortNamespaces() {
isSorted = true;
}
-void NamespaceSpecifierSet::AddNamespace(NamespaceDecl *ND) {
- DeclContext *Ctx = cast<DeclContext>(ND);
+static unsigned BuildNestedNameSpecifier(ASTContext &Context,
+ DeclContextList &DeclChain,
+ NestedNameSpecifier *&NNS) {
+ unsigned NumSpecifiers = 0;
+ for (DeclContextList::reverse_iterator C = DeclChain.rbegin(),
+ CEnd = DeclChain.rend();
+ C != CEnd; ++C) {
+ if (NamespaceDecl *ND = dyn_cast_or_null<NamespaceDecl>(*C)) {
+ NNS = NestedNameSpecifier::Create(Context, NNS, ND);
+ ++NumSpecifiers;
+ } else if (RecordDecl *RD = dyn_cast_or_null<RecordDecl>(*C)) {
+ NNS = NestedNameSpecifier::Create(Context, NNS, RD->isTemplateDecl(),
+ RD->getTypeForDecl());
+ ++NumSpecifiers;
+ }
+ }
+ return NumSpecifiers;
+}
+
+void NamespaceSpecifierSet::AddNameSpecifier(DeclContext *Ctx) {
NestedNameSpecifier *NNS = NULL;
unsigned NumSpecifiers = 0;
DeclContextList NamespaceDeclChain(BuildContextChain(Ctx));
@@ -3481,29 +3712,37 @@ void NamespaceSpecifierSet::AddNamespace(NamespaceDecl *ND) {
NamespaceDeclChain.pop_back();
}
+ // Build the NestedNameSpecifier from what is left of the NamespaceDeclChain
+ NumSpecifiers = BuildNestedNameSpecifier(Context, NamespaceDeclChain, NNS);
+
// Add an explicit leading '::' specifier if needed.
- if (NamespaceDecl *ND =
- NamespaceDeclChain.empty() ? NULL :
- dyn_cast_or_null<NamespaceDecl>(NamespaceDeclChain.back())) {
+ if (NamespaceDeclChain.empty()) {
+ // Rebuild the NestedNameSpecifier as a globally-qualified specifier.
+ NNS = NestedNameSpecifier::GlobalSpecifier(Context);
+ NumSpecifiers =
+ BuildNestedNameSpecifier(Context, FullNamespaceDeclChain, NNS);
+ } else if (NamedDecl *ND =
+ dyn_cast_or_null<NamedDecl>(NamespaceDeclChain.back())) {
IdentifierInfo *Name = ND->getIdentifier();
- if (std::find(CurContextIdentifiers.begin(), CurContextIdentifiers.end(),
- Name) != CurContextIdentifiers.end() ||
- std::find(CurNameSpecifierIdentifiers.begin(),
+ bool SameNameSpecifier = false;
+ if (std::find(CurNameSpecifierIdentifiers.begin(),
CurNameSpecifierIdentifiers.end(),
Name) != CurNameSpecifierIdentifiers.end()) {
- NamespaceDeclChain = FullNamespaceDeclChain;
- NNS = NestedNameSpecifier::GlobalSpecifier(Context);
+ std::string NewNameSpecifier;
+ llvm::raw_string_ostream SpecifierOStream(NewNameSpecifier);
+ SmallVector<const IdentifierInfo *, 4> NewNameSpecifierIdentifiers;
+ getNestedNameSpecifierIdentifiers(NNS, NewNameSpecifierIdentifiers);
+ NNS->print(SpecifierOStream, Context.getPrintingPolicy());
+ SpecifierOStream.flush();
+ SameNameSpecifier = NewNameSpecifier == CurNameSpecifier;
}
- }
-
- // Build the NestedNameSpecifier from what is left of the NamespaceDeclChain
- for (DeclContextList::reverse_iterator C = NamespaceDeclChain.rbegin(),
- CEnd = NamespaceDeclChain.rend();
- C != CEnd; ++C) {
- NamespaceDecl *ND = dyn_cast_or_null<NamespaceDecl>(*C);
- if (ND) {
- NNS = NestedNameSpecifier::Create(Context, NNS, ND);
- ++NumSpecifiers;
+ if (SameNameSpecifier ||
+ std::find(CurContextIdentifiers.begin(), CurContextIdentifiers.end(),
+ Name) != CurContextIdentifiers.end()) {
+ // Rebuild the NestedNameSpecifier as a globally-qualified specifier.
+ NNS = NestedNameSpecifier::GlobalSpecifier(Context);
+ NumSpecifiers =
+ BuildNestedNameSpecifier(Context, FullNamespaceDeclChain, NNS);
}
}
@@ -3515,8 +3754,8 @@ void NamespaceSpecifierSet::AddNamespace(NamespaceDecl *ND) {
SmallVector<const IdentifierInfo*, 4> NewNameSpecifierIdentifiers;
getNestedNameSpecifierIdentifiers(NNS, NewNameSpecifierIdentifiers);
NumSpecifiers = llvm::ComputeEditDistance(
- llvm::ArrayRef<const IdentifierInfo*>(CurNameSpecifierIdentifiers),
- llvm::ArrayRef<const IdentifierInfo*>(NewNameSpecifierIdentifiers));
+ ArrayRef<const IdentifierInfo *>(CurNameSpecifierIdentifiers),
+ ArrayRef<const IdentifierInfo *>(NewNameSpecifierIdentifiers));
}
isSorted = false;
@@ -3531,10 +3770,12 @@ static void LookupPotentialTypoResult(Sema &SemaRef,
Scope *S, CXXScopeSpec *SS,
DeclContext *MemberContext,
bool EnteringContext,
- bool isObjCIvarLookup) {
+ bool isObjCIvarLookup,
+ bool FindHidden) {
Res.suppressDiagnostics();
Res.clear();
Res.setLookupName(Name);
+ Res.setAllowHidden(FindHidden);
if (MemberContext) {
if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(MemberContext)) {
if (isObjCIvarLookup) {
@@ -3593,7 +3834,7 @@ static void AddKeywordsToConsumer(Sema &SemaRef,
if (CCC.WantTypeSpecifiers) {
// Add type-specifier keywords to the set of results.
- const char *CTypeSpecs[] = {
+ static const char *const CTypeSpecs[] = {
"char", "const", "double", "enum", "float", "int", "long", "short",
"signed", "struct", "union", "unsigned", "void", "volatile",
"_Complex", "_Imaginary",
@@ -3601,7 +3842,7 @@ static void AddKeywordsToConsumer(Sema &SemaRef,
"extern", "inline", "static", "typedef"
};
- const unsigned NumCTypeSpecs = sizeof(CTypeSpecs) / sizeof(CTypeSpecs[0]);
+ const unsigned NumCTypeSpecs = llvm::array_lengthof(CTypeSpecs);
for (unsigned I = 0; I != NumCTypeSpecs; ++I)
Consumer.addKeywordResult(CTypeSpecs[I]);
@@ -3645,10 +3886,10 @@ static void AddKeywordsToConsumer(Sema &SemaRef,
}
if (SemaRef.getLangOpts().CPlusPlus) {
- const char *CXXExprs[] = {
+ static const char *const CXXExprs[] = {
"delete", "new", "operator", "throw", "typeid"
};
- const unsigned NumCXXExprs = sizeof(CXXExprs) / sizeof(CXXExprs[0]);
+ const unsigned NumCXXExprs = llvm::array_lengthof(CXXExprs);
for (unsigned I = 0; I != NumCXXExprs; ++I)
Consumer.addKeywordResult(CXXExprs[I]);
@@ -3672,9 +3913,9 @@ static void AddKeywordsToConsumer(Sema &SemaRef,
if (CCC.WantRemainingKeywords) {
if (SemaRef.getCurFunctionOrMethodDecl() || SemaRef.getCurBlock()) {
// Statements.
- const char *CStmts[] = {
+ static const char *const CStmts[] = {
"do", "else", "for", "goto", "if", "return", "switch", "while" };
- const unsigned NumCStmts = sizeof(CStmts) / sizeof(CStmts[0]);
+ const unsigned NumCStmts = llvm::array_lengthof(CStmts);
for (unsigned I = 0; I != NumCStmts; ++I)
Consumer.addKeywordResult(CStmts[I]);
@@ -3725,6 +3966,50 @@ static bool isCandidateViable(CorrectionCandidateCallback &CCC,
return Candidate.getEditDistance(false) != TypoCorrection::InvalidDistance;
}
+/// \brief Check whether the declarations found for a typo correction are
+/// visible, and if none of them are, convert the correction to an 'import
+/// a module' correction.
+static void checkCorrectionVisibility(Sema &SemaRef, TypoCorrection &TC,
+ DeclarationName TypoName) {
+ if (TC.begin() == TC.end())
+ return;
+
+ TypoCorrection::decl_iterator DI = TC.begin(), DE = TC.end();
+
+ for (/**/; DI != DE; ++DI)
+ if (!LookupResult::isVisible(SemaRef, *DI))
+ break;
+ // Nothing to do if all decls are visible.
+ if (DI == DE)
+ return;
+
+ llvm::SmallVector<NamedDecl*, 4> NewDecls(TC.begin(), DI);
+ bool AnyVisibleDecls = !NewDecls.empty();
+
+ for (/**/; DI != DE; ++DI) {
+ NamedDecl *VisibleDecl = *DI;
+ if (!LookupResult::isVisible(SemaRef, *DI))
+ VisibleDecl = findAcceptableDecl(SemaRef, *DI);
+
+ if (VisibleDecl) {
+ if (!AnyVisibleDecls) {
+ // Found a visible decl, discard all hidden ones.
+ AnyVisibleDecls = true;
+ NewDecls.clear();
+ }
+ NewDecls.push_back(VisibleDecl);
+ } else if (!AnyVisibleDecls && !(*DI)->isModulePrivate())
+ NewDecls.push_back(*DI);
+ }
+
+ if (NewDecls.empty())
+ TC = TypoCorrection();
+ else {
+ TC.setCorrectionDecls(NewDecls);
+ TC.setRequiresImport(!AnyVisibleDecls);
+ }
+}
+
/// \brief Try to "correct" a typo in the source code by finding
/// visible declarations whose names are similar to the name that was
/// present in the source code.
@@ -3762,8 +4047,18 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
CorrectionCandidateCallback &CCC,
DeclContext *MemberContext,
bool EnteringContext,
- const ObjCObjectPointerType *OPT) {
- if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking)
+ const ObjCObjectPointerType *OPT,
+ bool RecordFailure) {
+ // Always let the ExternalSource have the first chance at correction, even
+ // if we would otherwise have given up.
+ if (ExternalSource) {
+ if (TypoCorrection Correction = ExternalSource->CorrectTypo(
+ TypoName, LookupKind, S, SS, CCC, MemberContext, EnteringContext, OPT))
+ return Correction;
+ }
+
+ if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking ||
+ DisableTypoCorrection)
return TypoCorrection();
// In Microsoft mode, don't perform typo correction in a template member
@@ -3792,6 +4087,18 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
if (S && S->isInObjcMethodScope() && Typo == getSuperIdentifier())
return TypoCorrection();
+ // Abort if typo correction already failed for this specific typo.
+ IdentifierSourceLocations::iterator locs = TypoCorrectionFailures.find(Typo);
+ if (locs != TypoCorrectionFailures.end() &&
+ locs->second.count(TypoName.getLoc()))
+ return TypoCorrection();
+
+ // Don't try to correct the identifier "vector" when in AltiVec mode.
+ // TODO: Figure out why typo correction misbehaves in this case, fix it, and
+ // remove this workaround.
+ if (getLangOpts().AltiVec && Typo->isStr("vector"))
+ return TypoCorrection();
+
NamespaceSpecifierSet Namespaces(Context, CurContext, SS);
TypoCorrectionConsumer Consumer(*this, Typo);
@@ -3836,8 +4143,15 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
// keyword case, we'll end up adding the keyword below.
if (Cached->second) {
if (!Cached->second.isKeyword() &&
- isCandidateViable(CCC, Cached->second))
- Consumer.addCorrection(Cached->second);
+ isCandidateViable(CCC, Cached->second)) {
+ // Do not use correction that is unaccessible in the given scope.
+ NamedDecl *CorrectionDecl = Cached->second.getCorrectionDecl();
+ DeclarationNameInfo NameInfo(CorrectionDecl->getDeclName(),
+ CorrectionDecl->getLocation());
+ LookupResult R(*this, NameInfo, LookupOrdinaryName);
+ if (LookupName(R, S))
+ Consumer.addCorrection(Cached->second);
+ }
} else {
// Only honor no-correction cache hits when a callback that will validate
// correction candidates is not being used.
@@ -3858,11 +4172,12 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
// corrections.
bool SearchNamespaces
= getLangOpts().CPlusPlus &&
- (IsUnqualifiedLookup || (QualifiedDC && QualifiedDC->isNamespace()));
- // In a few cases we *only* want to search for corrections bases on just
+ (IsUnqualifiedLookup || (SS && SS->isSet()));
+ // In a few cases we *only* want to search for corrections based on just
// adding or changing the nested name specifier.
- bool AllowOnlyNNSChanges = Typo->getName().size() < 3;
-
+ unsigned TypoLen = Typo->getName().size();
+ bool AllowOnlyNNSChanges = TypoLen < 3;
+
if (IsUnqualifiedLookup || SearchNamespaces) {
// For unqualified lookup, look through all of the names that we have
// seen in this translation unit.
@@ -3890,24 +4205,16 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
AddKeywordsToConsumer(*this, Consumer, S, CCC, SS && SS->isNotEmpty());
// If we haven't found anything, we're done.
- if (Consumer.empty()) {
- // If this was an unqualified lookup, note that no correction was found.
- if (IsUnqualifiedLookup)
- (void)UnqualifiedTyposCorrected[Typo];
-
- return TypoCorrection();
- }
+ if (Consumer.empty())
+ return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure,
+ IsUnqualifiedLookup);
// Make sure the best edit distance (prior to adding any namespace qualifiers)
// is not more that about a third of the length of the typo's identifier.
unsigned ED = Consumer.getBestEditDistance(true);
- if (ED > 0 && Typo->getName().size() / ED < 3) {
- // If this was an unqualified lookup, note that no correction was found.
- if (IsUnqualifiedLookup)
- (void)UnqualifiedTyposCorrected[Typo];
-
- return TypoCorrection();
- }
+ if (ED > 0 && TypoLen / ED < 3)
+ return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure,
+ IsUnqualifiedLookup);
// Build the NestedNameSpecifiers for the KnownNamespaces, if we're going
// to search those namespaces.
@@ -3920,12 +4227,24 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
for (unsigned I = 0, N = ExternalKnownNamespaces.size(); I != N; ++I)
KnownNamespaces[ExternalKnownNamespaces[I]] = true;
}
-
- for (llvm::MapVector<NamespaceDecl*, bool>::iterator
+
+ for (llvm::MapVector<NamespaceDecl*, bool>::iterator
KNI = KnownNamespaces.begin(),
KNIEnd = KnownNamespaces.end();
KNI != KNIEnd; ++KNI)
- Namespaces.AddNamespace(KNI->first);
+ Namespaces.AddNameSpecifier(KNI->first);
+
+ for (ASTContext::type_iterator TI = Context.types_begin(),
+ TIEnd = Context.types_end();
+ TI != TIEnd; ++TI) {
+ if (CXXRecordDecl *CD = (*TI)->getAsCXXRecordDecl()) {
+ CD = CD->getCanonicalDecl();
+ if (!CD->isDependentType() && !CD->isAnonymousStructOrUnion() &&
+ !CD->isUnion() &&
+ (CD->isBeingDefined() || CD->isCompleteDefinition()))
+ Namespaces.AddNameSpecifier(CD);
+ }
+ }
}
// Weed out any names that could not be found by name lookup or, if a
@@ -3935,7 +4254,6 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
TmpRes.suppressDiagnostics();
while (!Consumer.empty()) {
TypoCorrectionConsumer::distance_iterator DI = Consumer.begin();
- unsigned ED = DI->first;
for (TypoCorrectionConsumer::result_iterator I = DI->second.begin(),
IEnd = DI->second.end();
I != IEnd; /* Increment in loop. */) {
@@ -3976,13 +4294,31 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
// Perform name lookup on this name.
TypoCorrection &Candidate = I->second.front();
IdentifierInfo *Name = Candidate.getCorrectionAsIdentifierInfo();
- LookupPotentialTypoResult(*this, TmpRes, Name, S, SS, MemberContext,
- EnteringContext, CCC.IsObjCIvarLookup);
+ DeclContext *TempMemberContext = MemberContext;
+ CXXScopeSpec *TempSS = SS;
+retry_lookup:
+ LookupPotentialTypoResult(*this, TmpRes, Name, S, TempSS,
+ TempMemberContext, EnteringContext,
+ CCC.IsObjCIvarLookup,
+ Name == TypoName.getName() &&
+ !Candidate.WillReplaceSpecifier());
switch (TmpRes.getResultKind()) {
case LookupResult::NotFound:
case LookupResult::NotFoundInCurrentInstantiation:
case LookupResult::FoundUnresolvedValue:
+ if (TempSS) {
+ // Immediately retry the lookup without the given CXXScopeSpec
+ TempSS = NULL;
+ Candidate.WillReplaceSpecifier(true);
+ goto retry_lookup;
+ }
+ if (TempMemberContext) {
+ if (SS && !TempSS)
+ TempSS = SS;
+ TempMemberContext = NULL;
+ goto retry_lookup;
+ }
QualifiedResults.push_back(Candidate);
// We didn't find this name in our scope, or didn't like what we found;
// ignore it.
@@ -3996,7 +4332,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
case LookupResult::Ambiguous:
// We don't deal with ambiguities.
- return TypoCorrection();
+ return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
case LookupResult::FoundOverloaded: {
TypoCorrectionConsumer::result_iterator Prev = I;
@@ -4006,8 +4342,10 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
TRD != TRDEnd; ++TRD)
Candidate.addCorrectionDecl(*TRD);
++I;
- if (!isCandidateViable(CCC, Candidate))
+ if (!isCandidateViable(CCC, Candidate)) {
+ QualifiedResults.push_back(Candidate);
DI->second.erase(Prev);
+ }
break;
}
@@ -4015,8 +4353,10 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
TypoCorrectionConsumer::result_iterator Prev = I;
Candidate.setCorrectionDecl(TmpRes.getAsSingle<NamedDecl>());
++I;
- if (!isCandidateViable(CCC, Candidate))
+ if (!isCandidateViable(CCC, Candidate)) {
+ QualifiedResults.push_back(Candidate);
DI->second.erase(Prev);
+ }
break;
}
@@ -4025,7 +4365,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
if (DI->second.empty())
Consumer.erase(DI);
- else if (!getLangOpts().CPlusPlus || QualifiedResults.empty() || !ED)
+ else if (!getLangOpts().CPlusPlus || QualifiedResults.empty() || !DI->first)
// If there are results in the closest possible bucket, stop
break;
@@ -4040,10 +4380,31 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
NIEnd = Namespaces.end();
NI != NIEnd; ++NI) {
DeclContext *Ctx = NI->DeclCtx;
+ const Type *NSType = NI->NameSpecifier->getAsType();
+
+ // If the current NestedNameSpecifier refers to a class and the
+ // current correction candidate is the name of that class, then skip
+ // it as it is unlikely a qualified version of the class' constructor
+ // is an appropriate correction.
+ if (CXXRecordDecl *NSDecl =
+ NSType ? NSType->getAsCXXRecordDecl() : 0) {
+ if (NSDecl->getIdentifier() == QRI->getCorrectionAsIdentifierInfo())
+ continue;
+ }
- // FIXME: Stop searching once the namespaces are too far away to create
- // acceptable corrections for this identifier (since the namespaces
- // are sorted in ascending order by edit distance).
+ TypoCorrection TC(*QRI);
+ TC.ClearCorrectionDecls();
+ TC.setCorrectionSpecifier(NI->NameSpecifier);
+ TC.setQualifierDistance(NI->EditDistance);
+ TC.setCallbackDistance(0); // Reset the callback distance
+
+ // If the current correction candidate and namespace combination are
+ // too far away from the original typo based on the normalized edit
+ // distance, then skip performing a qualified name lookup.
+ unsigned TmpED = TC.getEditDistance(true);
+ if (QRI->getCorrectionAsIdentifierInfo() != Typo &&
+ TmpED && TypoLen / TmpED < 3)
+ continue;
TmpRes.clear();
TmpRes.setLookupName(QRI->getCorrectionAsIdentifierInfo());
@@ -4052,23 +4413,30 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
// Any corrections added below will be validated in subsequent
// iterations of the main while() loop over the Consumer's contents.
switch (TmpRes.getResultKind()) {
- case LookupResult::Found: {
- TypoCorrection TC(*QRI);
- TC.setCorrectionDecl(TmpRes.getAsSingle<NamedDecl>());
- TC.setCorrectionSpecifier(NI->NameSpecifier);
- TC.setQualifierDistance(NI->EditDistance);
- Consumer.addCorrection(TC);
- break;
- }
+ case LookupResult::Found:
case LookupResult::FoundOverloaded: {
- TypoCorrection TC(*QRI);
- TC.setCorrectionSpecifier(NI->NameSpecifier);
- TC.setQualifierDistance(NI->EditDistance);
+ if (SS && SS->isValid()) {
+ std::string NewQualified = TC.getAsString(getLangOpts());
+ std::string OldQualified;
+ llvm::raw_string_ostream OldOStream(OldQualified);
+ SS->getScopeRep()->print(OldOStream, getPrintingPolicy());
+ OldOStream << TypoName;
+ // If correction candidate would be an identical written qualified
+ // identifer, then the existing CXXScopeSpec probably included a
+ // typedef that didn't get accounted for properly.
+ if (OldOStream.str() == NewQualified)
+ break;
+ }
for (LookupResult::iterator TRD = TmpRes.begin(),
TRDEnd = TmpRes.end();
- TRD != TRDEnd; ++TRD)
- TC.addCorrectionDecl(*TRD);
- Consumer.addCorrection(TC);
+ TRD != TRDEnd; ++TRD) {
+ if (CheckMemberAccess(TC.getCorrectionRange().getBegin(),
+ NSType ? NSType->getAsCXXRecordDecl() : 0,
+ TRD.getPair()) == AR_accessible)
+ TC.addCorrectionDecl(*TRD);
+ }
+ if (TC.isResolved())
+ Consumer.addCorrection(TC);
break;
}
case LookupResult::NotFound:
@@ -4085,30 +4453,31 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
}
// No corrections remain...
- if (Consumer.empty()) return TypoCorrection();
+ if (Consumer.empty())
+ return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
TypoResultsMap &BestResults = Consumer.getBestResults();
ED = Consumer.getBestEditDistance(true);
- if (!AllowOnlyNNSChanges && ED > 0 && Typo->getName().size() / ED < 3) {
+ if (!AllowOnlyNNSChanges && ED > 0 && TypoLen / ED < 3) {
// If this was an unqualified lookup and we believe the callback
// object wouldn't have filtered out possible corrections, note
// that no correction was found.
- if (IsUnqualifiedLookup && !ValidatingCallback)
- (void)UnqualifiedTyposCorrected[Typo];
-
- return TypoCorrection();
+ return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure,
+ IsUnqualifiedLookup && !ValidatingCallback);
}
// If only a single name remains, return that result.
if (BestResults.size() == 1) {
const TypoResultList &CorrectionList = BestResults.begin()->second;
const TypoCorrection &Result = CorrectionList.front();
- if (CorrectionList.size() != 1) return TypoCorrection();
+ if (CorrectionList.size() != 1)
+ return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
// Don't correct to a keyword that's the same as the typo; the keyword
// wasn't actually in scope.
- if (ED == 0 && Result.isKeyword()) return TypoCorrection();
+ if (ED == 0 && Result.isKeyword())
+ return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
// Record the correction for unqualified lookup.
if (IsUnqualifiedLookup)
@@ -4116,6 +4485,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
TypoCorrection TC = Result;
TC.setCorrectionRange(SS, TypoName);
+ checkCorrectionVisibility(*this, TC, TypoName.getName());
return TC;
}
else if (BestResults.size() > 1
@@ -4130,7 +4500,8 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
// Don't correct to a keyword that's the same as the typo; the keyword
// wasn't actually in scope.
- if (ED == 0) return TypoCorrection();
+ if (ED == 0)
+ return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
// Record the correction for unqualified lookup.
if (IsUnqualifiedLookup)
@@ -4146,7 +4517,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName,
if (IsUnqualifiedLookup && !ValidatingCallback)
(void)UnqualifiedTyposCorrected[Typo];
- return TypoCorrection();
+ return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure);
}
void TypoCorrection::addCorrectionDecl(NamedDecl *CDecl) {
@@ -4166,7 +4537,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();
}
@@ -4190,3 +4561,122 @@ bool CorrectionCandidateCallback::ValidateCandidate(const TypoCorrection &candid
return WantTypeSpecifiers;
}
+
+FunctionCallFilterCCC::FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
+ bool HasExplicitTemplateArgs)
+ : NumArgs(NumArgs), HasExplicitTemplateArgs(HasExplicitTemplateArgs) {
+ WantTypeSpecifiers = SemaRef.getLangOpts().CPlusPlus;
+ WantRemainingKeywords = false;
+}
+
+bool FunctionCallFilterCCC::ValidateCandidate(const TypoCorrection &candidate) {
+ if (!candidate.getCorrectionDecl())
+ return candidate.isKeyword();
+
+ for (TypoCorrection::const_decl_iterator DI = candidate.begin(),
+ DIEnd = candidate.end();
+ DI != DIEnd; ++DI) {
+ FunctionDecl *FD = 0;
+ NamedDecl *ND = (*DI)->getUnderlyingDecl();
+ if (FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(ND))
+ FD = FTD->getTemplatedDecl();
+ if (!HasExplicitTemplateArgs && !FD) {
+ if (!(FD = dyn_cast<FunctionDecl>(ND)) && isa<ValueDecl>(ND)) {
+ // If the Decl is neither a function nor a template function,
+ // determine if it is a pointer or reference to a function. If so,
+ // check against the number of arguments expected for the pointee.
+ QualType ValType = cast<ValueDecl>(ND)->getType();
+ if (ValType->isAnyPointerType() || ValType->isReferenceType())
+ ValType = ValType->getPointeeType();
+ if (const FunctionProtoType *FPT = ValType->getAs<FunctionProtoType>())
+ if (FPT->getNumArgs() == NumArgs)
+ return true;
+ }
+ }
+ if (FD && FD->getNumParams() >= NumArgs &&
+ FD->getMinRequiredArguments() <= NumArgs)
+ return true;
+ }
+ return false;
+}
+
+void Sema::diagnoseTypo(const TypoCorrection &Correction,
+ const PartialDiagnostic &TypoDiag,
+ bool ErrorRecovery) {
+ diagnoseTypo(Correction, TypoDiag, PDiag(diag::note_previous_decl),
+ ErrorRecovery);
+}
+
+/// Find which declaration we should import to provide the definition of
+/// the given declaration.
+static const NamedDecl *getDefinitionToImport(const NamedDecl *D) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(D))
+ return VD->getDefinition();
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
+ return FD->isDefined(FD) ? FD : 0;
+ if (const TagDecl *TD = dyn_cast<TagDecl>(D))
+ return TD->getDefinition();
+ if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D))
+ return ID->getDefinition();
+ if (const ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D))
+ return PD->getDefinition();
+ if (const TemplateDecl *TD = dyn_cast<TemplateDecl>(D))
+ return getDefinitionToImport(TD->getTemplatedDecl());
+ return 0;
+}
+
+/// \brief Diagnose a successfully-corrected typo. Separated from the correction
+/// itself to allow external validation of the result, etc.
+///
+/// \param Correction The result of performing typo correction.
+/// \param TypoDiag The diagnostic to produce. This will have the corrected
+/// string added to it (and usually also a fixit).
+/// \param PrevNote A note to use when indicating the location of the entity to
+/// which we are correcting. Will have the correction string added to it.
+/// \param ErrorRecovery If \c true (the default), the caller is going to
+/// recover from the typo as if the corrected string had been typed.
+/// In this case, \c PDiag must be an error, and we will attach a fixit
+/// to it.
+void Sema::diagnoseTypo(const TypoCorrection &Correction,
+ const PartialDiagnostic &TypoDiag,
+ const PartialDiagnostic &PrevNote,
+ bool ErrorRecovery) {
+ std::string CorrectedStr = Correction.getAsString(getLangOpts());
+ std::string CorrectedQuotedStr = Correction.getQuoted(getLangOpts());
+ FixItHint FixTypo = FixItHint::CreateReplacement(
+ Correction.getCorrectionRange(), CorrectedStr);
+
+ // Maybe we're just missing a module import.
+ if (Correction.requiresImport()) {
+ NamedDecl *Decl = Correction.getCorrectionDecl();
+ assert(Decl && "import required but no declaration to import");
+
+ // Suggest importing a module providing the definition of this entity, if
+ // possible.
+ const NamedDecl *Def = getDefinitionToImport(Decl);
+ if (!Def)
+ Def = Decl;
+ Module *Owner = Def->getOwningModule();
+ assert(Owner && "definition of hidden declaration is not in a module");
+
+ Diag(Correction.getCorrectionRange().getBegin(),
+ diag::err_module_private_declaration)
+ << Def << Owner->getFullModuleName();
+ Diag(Def->getLocation(), diag::note_previous_declaration);
+
+ // Recover by implicitly importing this module.
+ if (!isSFINAEContext() && ErrorRecovery)
+ createImplicitModuleImport(Correction.getCorrectionRange().getBegin(),
+ Owner);
+ return;
+ }
+
+ Diag(Correction.getCorrectionRange().getBegin(), TypoDiag)
+ << CorrectedQuotedStr << (ErrorRecovery ? FixTypo : FixItHint());
+
+ NamedDecl *ChosenDecl =
+ Correction.isKeyword() ? 0 : Correction.getCorrectionDecl();
+ if (PrevNote.getDiagID() && ChosenDecl)
+ Diag(ChosenDecl->getLocation(), PrevNote)
+ << CorrectedQuotedStr << (ErrorRecovery ? FixItHint() : FixTypo);
+}
OpenPOWER on IntegriCloud