Skip to content

Commit

Permalink
Merged master:e841029aef7 into amd-gfx:fd06200fd90
Browse files Browse the repository at this point in the history
Local branch amd-gfx fd06200 Merged master:bae5aac1ff4 into amd-gfx:bb5eee37b2f
Remote branch master e841029 [clangd] Fix diagnostic location for macro expansions
  • Loading branch information
Sw authored and Sw committed Nov 25, 2019
2 parents fd06200 + e841029 commit 5b66d8d
Show file tree
Hide file tree
Showing 8 changed files with 460 additions and 22 deletions.
123 changes: 115 additions & 8 deletions clang-tools-extra/clangd/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "SourceCode.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/NestedNameSpecifier.h"
Expand All @@ -22,10 +23,15 @@
#include "clang/Basic/Specifiers.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
#include <vector>

namespace clang {
namespace clangd {
Expand Down Expand Up @@ -67,6 +73,74 @@ bool isTemplateSpecializationKind(const NamedDecl *D,
isTemplateSpecializationKind<VarDecl>(D, Kind);
}

// Store all UsingDirectiveDecls in parent contexts of DestContext, that were
// introduced before InsertionPoint.
llvm::DenseSet<const NamespaceDecl *>
getUsingNamespaceDirectives(const DeclContext *DestContext,
SourceLocation Until) {
const auto &SM = DestContext->getParentASTContext().getSourceManager();
llvm::DenseSet<const NamespaceDecl *> VisibleNamespaceDecls;
for (const auto *DC = DestContext; DC; DC = DC->getLookupParent()) {
for (const auto *D : DC->decls()) {
if (!SM.isWrittenInSameFile(D->getLocation(), Until) ||
!SM.isBeforeInTranslationUnit(D->getLocation(), Until))
continue;
if (auto *UDD = llvm::dyn_cast<UsingDirectiveDecl>(D))
VisibleNamespaceDecls.insert(
UDD->getNominatedNamespace()->getCanonicalDecl());
}
}
return VisibleNamespaceDecls;
}

// Goes over all parents of SourceContext until we find a comman ancestor for
// DestContext and SourceContext. Any qualifier including and above common
// ancestor is redundant, therefore we stop at lowest common ancestor.
// In addition to that stops early whenever IsVisible returns true. This can be
// used to implement support for "using namespace" decls.
std::string
getQualification(ASTContext &Context, const DeclContext *DestContext,
const DeclContext *SourceContext,
llvm::function_ref<bool(NestedNameSpecifier *)> IsVisible) {
std::vector<const NestedNameSpecifier *> Parents;
bool ReachedNS = false;
for (const DeclContext *CurContext = SourceContext; CurContext;
CurContext = CurContext->getLookupParent()) {
// Stop once we reach a common ancestor.
if (CurContext->Encloses(DestContext))
break;

NestedNameSpecifier *NNS = nullptr;
if (auto *TD = llvm::dyn_cast<TagDecl>(CurContext)) {
// There can't be any more tag parents after hitting a namespace.
assert(!ReachedNS);
NNS = NestedNameSpecifier::Create(Context, nullptr, false,
TD->getTypeForDecl());
} else {
ReachedNS = true;
auto *NSD = llvm::cast<NamespaceDecl>(CurContext);
NNS = NestedNameSpecifier::Create(Context, nullptr, NSD);
// Anonymous and inline namespace names are not spelled while qualifying a
// name, so skip those.
if (NSD->isAnonymousNamespace() || NSD->isInlineNamespace())
continue;
}
// Stop if this namespace is already visible at DestContext.
if (IsVisible(NNS))
break;

Parents.push_back(NNS);
}

// Go over name-specifiers in reverse order to create necessary qualification,
// since we stored inner-most parent first.
std::string Result;
llvm::raw_string_ostream OS(Result);
for (const auto *Parent : llvm::reverse(Parents))
Parent->print(OS, Context.getPrintingPolicy());
return OS.str();
}

} // namespace

bool isImplicitTemplateInstantiation(const NamedDecl *D) {
Expand All @@ -82,9 +156,7 @@ bool isImplementationDetail(const Decl *D) {
D->getASTContext().getSourceManager());
}

SourceLocation findName(const clang::Decl *D) {
return D->getLocation();
}
SourceLocation findName(const clang::Decl *D) { return D->getLocation(); }

std::string printQualifiedName(const NamedDecl &ND) {
std::string QName;
Expand Down Expand Up @@ -229,7 +301,7 @@ std::string shortenNamespace(const llvm::StringRef OriginalName,

unsigned DifferentAt = 0;
while (DifferentAt < MinLength &&
CurrentParts[DifferentAt] == OriginalParts[DifferentAt]) {
CurrentParts[DifferentAt] == OriginalParts[DifferentAt]) {
DifferentAt++;
}

Expand All @@ -239,13 +311,11 @@ std::string shortenNamespace(const llvm::StringRef OriginalName,
return join(Result, "::");
}

std::string printType(const QualType QT, const DeclContext & Context){
std::string printType(const QualType QT, const DeclContext &Context) {
PrintingPolicy PP(Context.getParentASTContext().getPrintingPolicy());
PP.SuppressUnwrittenScope = 1;
PP.SuppressTagKeyword = 1;
return shortenNamespace(
QT.getAsString(PP),
printNamespaceScope(Context) );
return shortenNamespace(QT.getAsString(PP), printNamespaceScope(Context));
}

QualType declaredType(const TypeDecl *D) {
Expand Down Expand Up @@ -364,5 +434,42 @@ llvm::Optional<QualType> getDeducedType(ASTContext &ASTCtx,
return V.DeducedType;
}

std::string getQualification(ASTContext &Context,
const DeclContext *DestContext,
SourceLocation InsertionPoint,
const NamedDecl *ND) {
auto VisibleNamespaceDecls =
getUsingNamespaceDirectives(DestContext, InsertionPoint);
return getQualification(
Context, DestContext, ND->getDeclContext(),
[&](NestedNameSpecifier *NNS) {
if (NNS->getKind() != NestedNameSpecifier::Namespace)
return false;
const auto *CanonNSD = NNS->getAsNamespace()->getCanonicalDecl();
return llvm::any_of(VisibleNamespaceDecls,
[CanonNSD](const NamespaceDecl *NSD) {
return NSD->getCanonicalDecl() == CanonNSD;
});
});
}

std::string getQualification(ASTContext &Context,
const DeclContext *DestContext,
SourceLocation InsertionPoint, const NamedDecl *ND,
llvm::ArrayRef<std::string> VisibleNamespaces) {
for (llvm::StringRef NS : VisibleNamespaces)
assert(NS.endswith("::"));
return getQualification(
Context, DestContext, ND->getDeclContext(),
[&](NestedNameSpecifier *NNS) {
return llvm::any_of(VisibleNamespaces, [&](llvm::StringRef Namespace) {
std::string NS;
llvm::raw_string_ostream OS(NS);
NNS->print(OS, Context.getPrintingPolicy());
return OS.str() == Namespace;
});
});
}

} // namespace clangd
} // namespace clang
41 changes: 40 additions & 1 deletion clang-tools-extra/clangd/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@

#include "index/SymbolID.h"
#include "clang/AST/Decl.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/MacroInfo.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringRef.h"
#include <string>
#include <vector>

namespace clang {
class SourceManager;
Expand Down Expand Up @@ -76,7 +80,7 @@ llvm::Optional<SymbolID> getSymbolID(const llvm::StringRef MacroName,

/// Returns a QualType as string. The result doesn't contain unwritten scopes
/// like annoymous/inline namespace.
std::string printType(const QualType QT, const DeclContext & Context);
std::string printType(const QualType QT, const DeclContext &Context);

/// Try to shorten the OriginalName by removing namespaces from the left of
/// the string that are redundant in the CurrentNamespace. This way the type
Expand Down Expand Up @@ -121,6 +125,41 @@ QualType declaredType(const TypeDecl *D);
/// It will return the underlying type.
llvm::Optional<QualType> getDeducedType(ASTContext &, SourceLocation Loc);

/// Gets the nested name specifier necessary for spelling \p ND in \p
/// DestContext, at \p InsertionPoint. It selects the shortest suffix of \p ND
/// such that it is visible in \p DestContext.
/// Returns an empty string if no qualification is necessary. For example, if
/// you want to qualify clang::clangd::bar::foo in clang::clangd::x, this
/// function will return bar. Note that the result might be sub-optimal for
/// classes, e.g. when the \p ND is a member of the base class.
///
/// This version considers all the using namespace directives before \p
/// InsertionPoint. i.e, if you have `using namespace
/// clang::clangd::bar`, this function will return an empty string for the
/// example above since no qualification is necessary in that case.
/// FIXME: Also take using directives and namespace aliases inside function body
/// into account.
std::string getQualification(ASTContext &Context,
const DeclContext *DestContext,
SourceLocation InsertionPoint,
const NamedDecl *ND);

/// This function uses the \p VisibleNamespaces to figure out if a shorter
/// qualification is sufficient for \p ND, and ignores any using namespace
/// directives. It can be useful if there's no AST for the DestContext, but some
/// pseudo-parsing is done. i.e. if \p ND is ns1::ns2::X and \p DestContext is
/// ns1::, users can provide `ns2::` as visible to change the result to be
/// empty.
/// Elements in VisibleNamespaces should be in the form: `ns::`, with trailing
/// "::".
/// Note that this is just textual and might be incorrect. e.g. when there are
/// two namespaces ns1::a and ns2::a, the function will early exit if "a::" is
/// present in \p VisibleNamespaces, no matter whether it is from ns1:: or ns2::
std::string getQualification(ASTContext &Context,
const DeclContext *DestContext,
SourceLocation InsertionPoint, const NamedDecl *ND,
llvm::ArrayRef<std::string> VisibleNamespaces);

} // namespace clangd
} // namespace clang

Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/Diagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ bool adjustDiagFromHeader(Diag &D, const clang::Diagnostic &Info,
if (D.Severity < DiagnosticsEngine::Level::Error)
return false;

const SourceLocation &DiagLoc = Info.getLocation();
const SourceManager &SM = Info.getSourceManager();
const SourceLocation &DiagLoc = SM.getExpansionLoc(Info.getLocation());
SourceLocation IncludeInMainFile;
auto GetIncludeLoc = [&SM](SourceLocation SLoc) {
return SM.getIncludeLoc(SM.getFileID(SLoc));
Expand Down
17 changes: 10 additions & 7 deletions clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,10 @@ bool checkDeclsAreVisible(const llvm::DenseSet<const Decl *> &DeclRefs,
return true;
}

// Rewrites body of FD to fully-qualify all of the decls inside.
llvm::Expected<std::string> qualifyAllDecls(const FunctionDecl *FD) {
// Rewrites body of FD by re-spelling all of the names to make sure they are
// still valid in context of Target.
llvm::Expected<std::string> qualifyAllDecls(const FunctionDecl *FD,
const FunctionDecl *Target) {
// There are three types of spellings that needs to be qualified in a function
// body:
// - Types: Foo -> ns::Foo
Expand All @@ -147,16 +149,16 @@ llvm::Expected<std::string> qualifyAllDecls(const FunctionDecl *FD) {
// using ns3 = ns2 -> using ns3 = ns1::ns2
//
// Go over all references inside a function body to generate replacements that
// will fully qualify those. So that body can be moved into an arbitrary file.
// will qualify those. So that body can be moved into an arbitrary file.
// We perform the qualification by qualyfying the first type/decl in a
// (un)qualified name. e.g:
// namespace a { namespace b { class Bar{}; void foo(); } }
// b::Bar x; -> a::b::Bar x;
// foo(); -> a::b::foo();
// FIXME: Instead of fully qualyfying we should try deducing visible scopes at
// target location and generate minimal edits.

auto *TargetContext = Target->getLexicalDeclContext();
const SourceManager &SM = FD->getASTContext().getSourceManager();

tooling::Replacements Replacements;
bool HadErrors = false;
findExplicitReferences(FD->getBody(), [&](ReferenceLoc Ref) {
Expand Down Expand Up @@ -193,7 +195,8 @@ llvm::Expected<std::string> qualifyAllDecls(const FunctionDecl *FD) {
if (!ND->getDeclContext()->isNamespace())
return;

std::string Qualifier = printNamespaceScope(*ND->getDeclContext());
const std::string Qualifier = getQualification(
FD->getASTContext(), TargetContext, Target->getBeginLoc(), ND);
if (auto Err = Replacements.add(
tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier))) {
HadErrors = true;
Expand Down Expand Up @@ -437,7 +440,7 @@ class DefineInline : public Tweak {
if (!ParamReplacements)
return ParamReplacements.takeError();

auto QualifiedBody = qualifyAllDecls(Source);
auto QualifiedBody = qualifyAllDecls(Source, Target);
if (!QualifiedBody)
return QualifiedBody.takeError();

Expand Down
Loading

0 comments on commit 5b66d8d

Please sign in to comment.