зеркало из https://github.com/microsoft/clang-1.git
Improve diagnostics and recovery when the nested-name-specifier of a
qualified name does not actually refer into a class/class template/class template partial specialization. Improve printing of nested-name-specifiers to eliminate redudant qualifiers. Also, make it possible to output a nested-name-specifier through a DiagnosticBuilder, although there are relatively few places that will use this leeway. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@80056 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
ec7738776b
Коммит
dacd434c49
|
@ -14,6 +14,7 @@
|
|||
#ifndef LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H
|
||||
#define LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H
|
||||
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
|
||||
|
@ -179,6 +180,15 @@ public:
|
|||
void dump(const LangOptions &LO);
|
||||
};
|
||||
|
||||
/// Insertion operator for diagnostics. This allows sending NestedNameSpecifiers
|
||||
/// into a diagnostic with <<.
|
||||
inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
|
||||
NestedNameSpecifier *NNS) {
|
||||
DB.AddTaggedVal(reinterpret_cast<intptr_t>(NNS),
|
||||
Diagnostic::ak_nestednamespec);
|
||||
return DB;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -156,7 +156,8 @@ public:
|
|||
ak_identifierinfo, // IdentifierInfo
|
||||
ak_qualtype, // QualType
|
||||
ak_declarationname, // DeclarationName
|
||||
ak_nameddecl // NamedDecl *
|
||||
ak_nameddecl, // NamedDecl *
|
||||
ak_nestednamespec // NestedNameSpecifier *
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
|
@ -889,8 +889,8 @@ def err_template_spec_extra_headers : Error<
|
|||
"extraneous template parameter list in template specialization or "
|
||||
"out-of-line template definition">;
|
||||
def err_template_qualified_declarator_no_match : Error<
|
||||
"nested name specifier '%0' for declaration does not refer to a class "
|
||||
"template or class template partial specialization">;
|
||||
"nested name specifier %0 for declaration does not refer into a class, "
|
||||
"class template or class template partial specialization">;
|
||||
def err_template_spec_decl_out_of_scope_global : Error<
|
||||
"class template %select{|partial }0specialization of %1 must occur in the "
|
||||
"global scope">;
|
||||
|
|
|
@ -131,15 +131,33 @@ NestedNameSpecifier::print(llvm::raw_ostream &OS,
|
|||
std::string TypeStr;
|
||||
Type *T = getAsType();
|
||||
|
||||
// If this is a qualified name type, suppress the qualification:
|
||||
// it's part of our nested-name-specifier sequence anyway. FIXME:
|
||||
// We should be able to assert that this doesn't happen.
|
||||
if (const QualifiedNameType *QualT = dyn_cast<QualifiedNameType>(T))
|
||||
T = QualT->getNamedType().getTypePtr();
|
||||
|
||||
PrintingPolicy InnerPolicy(Policy);
|
||||
InnerPolicy.SuppressTagKind = true;
|
||||
|
||||
// Nested-name-specifiers are intended to contain minimally-qualified
|
||||
// types. An actual QualifiedNameType will not occur, since we'll store
|
||||
// just the type that is referred to in the nested-name-specifier (e.g.,
|
||||
// a TypedefType, TagType, etc.). However, when we are dealing with
|
||||
// dependent template-id types (e.g., Outer<T>::template Inner<U>),
|
||||
// the type requires its own nested-name-specifier for uniqueness, so we
|
||||
// suppress that nested-name-specifier during printing.
|
||||
assert(!isa<QualifiedNameType>(T) &&
|
||||
"Qualified name type in nested-name-specifier");
|
||||
if (const TemplateSpecializationType *SpecType
|
||||
= dyn_cast<TemplateSpecializationType>(T)) {
|
||||
// Print the template name without its corresponding
|
||||
// nested-name-specifier.
|
||||
SpecType->getTemplateName().print(OS, InnerPolicy, true);
|
||||
|
||||
// Print the template argument list.
|
||||
TypeStr = TemplateSpecializationType::PrintTemplateArgumentList(
|
||||
SpecType->getArgs(),
|
||||
SpecType->getNumArgs(),
|
||||
InnerPolicy);
|
||||
} else {
|
||||
// Print the type normally
|
||||
T->getAsStringInternal(TypeStr, InnerPolicy);
|
||||
}
|
||||
OS << TypeStr;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -797,6 +797,7 @@ FormatDiagnostic(llvm::SmallVectorImpl<char> &OutStr) const {
|
|||
case Diagnostic::ak_qualtype:
|
||||
case Diagnostic::ak_declarationname:
|
||||
case Diagnostic::ak_nameddecl:
|
||||
case Diagnostic::ak_nestednamespec:
|
||||
getDiags()->ConvertArgToString(getArgKind(ArgNo), getRawArg(ArgNo),
|
||||
Modifier, ModifierLen,
|
||||
Argument, ArgumentLen, OutStr);
|
||||
|
|
|
@ -79,8 +79,7 @@ static void ConvertArgToStringFn(Diagnostic::ArgumentKind Kind, intptr_t Val,
|
|||
else
|
||||
assert(ModLen == 0 && ArgLen == 0 &&
|
||||
"Invalid modifier for DeclarationName argument");
|
||||
} else {
|
||||
assert(Kind == Diagnostic::ak_nameddecl);
|
||||
} else if (Kind == Diagnostic::ak_nameddecl) {
|
||||
if (ModLen == 1 && Modifier[0] == 'q' && ArgLen == 0)
|
||||
S = reinterpret_cast<NamedDecl*>(Val)->getQualifiedNameAsString();
|
||||
else {
|
||||
|
@ -88,6 +87,11 @@ static void ConvertArgToStringFn(Diagnostic::ArgumentKind Kind, intptr_t Val,
|
|||
"Invalid modifier for NamedDecl* argument");
|
||||
S = reinterpret_cast<NamedDecl*>(Val)->getNameAsString();
|
||||
}
|
||||
} else {
|
||||
llvm::raw_string_ostream OS(S);
|
||||
assert(Kind == Diagnostic::ak_nestednamespec);
|
||||
reinterpret_cast<NestedNameSpecifier*> (Val)->print(OS,
|
||||
Context.PrintingPolicy);
|
||||
}
|
||||
|
||||
Output.push_back('\'');
|
||||
|
|
|
@ -79,17 +79,6 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
|
|||
// The nested name specifier refers to a member of a class template.
|
||||
return RecordT->getDecl();
|
||||
}
|
||||
|
||||
std::string NNSString;
|
||||
{
|
||||
llvm::raw_string_ostream OS(NNSString);
|
||||
NNS->print(OS, Context.PrintingPolicy);
|
||||
}
|
||||
|
||||
// FIXME: Allow us to pass a nested-name-specifier to Diag?
|
||||
Diag(SS.getRange().getBegin(),
|
||||
diag::err_template_qualified_declarator_no_match)
|
||||
<< NNSString << SS.getRange();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -298,6 +287,9 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
|||
T.getTypePtr());
|
||||
}
|
||||
|
||||
// FIXME: It would be nice to maintain the namespace alias name, then
|
||||
// see through that alias when resolving the nested-name-specifier down to
|
||||
// a declaration context.
|
||||
if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD))
|
||||
return NestedNameSpecifier::Create(Context, Prefix,
|
||||
Alias->getNamespace());
|
||||
|
@ -404,8 +396,6 @@ void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
|
|||
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
|
||||
if (DeclContext *DC = computeDeclContext(SS, true))
|
||||
EnterDeclaratorContext(S, DC);
|
||||
else
|
||||
const_cast<CXXScopeSpec&>(SS).setScopeRep(0);
|
||||
}
|
||||
|
||||
/// ActOnCXXExitDeclaratorScope - Called when a declarator that previously
|
||||
|
@ -415,8 +405,8 @@ void Sema::ActOnCXXEnterDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
|
|||
/// defining scope.
|
||||
void Sema::ActOnCXXExitDeclaratorScope(Scope *S, const CXXScopeSpec &SS) {
|
||||
assert(SS.isSet() && "Parser passed invalid CXXScopeSpec.");
|
||||
assert((SS.isInvalid() || S->getEntity() == computeDeclContext(SS, true)) &&
|
||||
"Context imbalance!");
|
||||
if (!SS.isInvalid())
|
||||
if (SS.isInvalid())
|
||||
return;
|
||||
if (computeDeclContext(SS, true))
|
||||
ExitDeclaratorContext(S);
|
||||
}
|
||||
|
|
|
@ -1701,7 +1701,19 @@ Sema::HandleDeclarator(Scope *S, Declarator &D,
|
|||
D.getIdentifierLoc());
|
||||
} else { // Something like "int foo::x;"
|
||||
DC = computeDeclContext(D.getCXXScopeSpec(), true);
|
||||
// FIXME: RequireCompleteDeclContext(D.getCXXScopeSpec()); ?
|
||||
|
||||
if (!DC) {
|
||||
// If we could not compute the declaration context, it's because the
|
||||
// declaration context is dependent but does not refer to a class,
|
||||
// class template, or class template partial specialization. Complain
|
||||
// and return early, to avoid the coming semantic disaster.
|
||||
Diag(D.getIdentifierLoc(),
|
||||
diag::err_template_qualified_declarator_no_match)
|
||||
<< (NestedNameSpecifier*)D.getCXXScopeSpec().getScopeRep()
|
||||
<< D.getCXXScopeSpec().getRange();
|
||||
return DeclPtrTy();
|
||||
}
|
||||
|
||||
PrevDecl = LookupQualifiedName(DC, Name, LookupOrdinaryName, true);
|
||||
|
||||
// C++ 7.3.1.2p2:
|
||||
|
|
|
@ -43,7 +43,7 @@ void X0<X, Y>::f3(size_type) const {
|
|||
}
|
||||
|
||||
template<class X, class Y>
|
||||
void X0<Y, X>::f4() { } // expected-error{{does not refer to}}
|
||||
void X0<Y, X>::f4() { } // expected-error{{does not refer}}
|
||||
|
||||
// FIXME: error message should probably say, "redefinition of 'X0<T, U>::f0'"
|
||||
// rather than just "redefinition of 'f0'"
|
||||
|
|
|
@ -87,3 +87,7 @@ Y Outer<X>::Inner1<Y>::value2 = Y();
|
|||
template<typename X>
|
||||
template<typename Y>
|
||||
Y Outer<X>::Inner1<Y>::ReallyInner::value3 = Y();
|
||||
|
||||
template<typename X>
|
||||
template<typename Y>
|
||||
Y Outer<X>::Inner1<Y*>::ReallyInner::value4; // expected-error{{Outer<X>::Inner1<Y *>::ReallyInner::}}
|
||||
|
|
Загрузка…
Ссылка в новой задаче