diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-08-26 00:04:55 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-08-26 00:04:55 +0000 |
commit | dacd434c49658286c380c7b4c357d76d53cb4aa1 (patch) | |
tree | 96445b18410c013f21aabb19f3fe17348fbfb225 | |
parent | ec7738776ba68576db5d8316af399d1f983245ee (diff) |
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
-rw-r--r-- | include/clang/AST/NestedNameSpecifier.h | 10 | ||||
-rw-r--r-- | include/clang/Basic/Diagnostic.h | 3 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
-rw-r--r-- | lib/AST/NestedNameSpecifier.cpp | 32 | ||||
-rw-r--r-- | lib/Basic/Diagnostic.cpp | 1 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 8 | ||||
-rw-r--r-- | lib/Sema/SemaCXXScopeSpec.cpp | 22 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 16 | ||||
-rw-r--r-- | test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp | 2 | ||||
-rw-r--r-- | test/SemaTemplate/nested-template.cpp | 4 |
10 files changed, 71 insertions, 31 deletions
diff --git a/include/clang/AST/NestedNameSpecifier.h b/include/clang/AST/NestedNameSpecifier.h index a1c48ffda5..0c31dbbf1a 100644 --- a/include/clang/AST/NestedNameSpecifier.h +++ b/include/clang/AST/NestedNameSpecifier.h @@ -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 diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index cdfa7e1157..8a09fa59ab 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -156,7 +156,8 @@ public: ak_identifierinfo, // IdentifierInfo ak_qualtype, // QualType ak_declarationname, // DeclarationName - ak_nameddecl // NamedDecl * + ak_nameddecl, // NamedDecl * + ak_nestednamespec // NestedNameSpecifier * }; private: diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 8b717c5a7b..1230f5b9d4 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/lib/AST/NestedNameSpecifier.cpp b/lib/AST/NestedNameSpecifier.cpp index 90ec4d33fd..0376f9db6b 100644 --- a/lib/AST/NestedNameSpecifier.cpp +++ b/lib/AST/NestedNameSpecifier.cpp @@ -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; - T->getAsStringInternal(TypeStr, InnerPolicy); + + // 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; } diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index 6ba5907a34..922702fdf5 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -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); diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 5425c4fb5a..24c5e09f43 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -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('\''); diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index 9c9bff8a08..3dac5d7297 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -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); } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index d478c048b8..fec9a075ec 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1633,7 +1633,7 @@ Sema::HandleDeclarator(Scope *S, Declarator &D, << D.getDeclSpec().getSourceRange() << D.getSourceRange(); return DeclPtrTy(); } - + // The scope passed in may not be a decl scope. Zip up the scope tree until // we find one that is. while ((S->getFlags() & Scope::DeclScope) == 0 || @@ -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: diff --git a/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp b/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp index 79f379aba1..28a5d98540 100644 --- a/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp +++ b/test/CXX/temp/temp.decls/temp.class/temp.mem.func/p1.cpp @@ -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'" diff --git a/test/SemaTemplate/nested-template.cpp b/test/SemaTemplate/nested-template.cpp index 3a7634f53b..896eb72398 100644 --- a/test/SemaTemplate/nested-template.cpp +++ b/test/SemaTemplate/nested-template.cpp @@ -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::}} |