cling/lib/Interpreter/AutoloadCallback.cpp

302 lines
9.9 KiB
C++
Raw Normal View History

2014-08-06 15:05:09 +02:00
//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author: Manasij Mukherjee <manasij7479@gmail.com>
// author: Vassil Vassilev <vvasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/Sema/Sema.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h" // for operator new[](unsigned long, ASTCtx..)
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/CompilerInstance.h"
2014-06-08 22:12:58 +05:30
#include "cling/Interpreter/Interpreter.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/AutoloadCallback.h"
#include "cling/Interpreter/Transaction.h"
#include "DeclUnloader.h"
namespace {
static const char annoTag[] = "$clingAutoload$";
static const size_t lenAnnoTag = sizeof(annoTag) - 1;
}
using namespace clang;
namespace cling {
void AutoloadCallback::report(clang::SourceLocation l, llvm::StringRef name,
llvm::StringRef header) {
Sema& sema= m_Interpreter->getSema();
unsigned id
= sema.getDiagnostics().getCustomDiagID (DiagnosticsEngine::Level::Warning,
"Note: '%0' can be found in %1");
/* unsigned idn //TODO: To be enabled after we have a way to get the full path
= sema.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Level::Note,
"Type : %0 , Full Path: %1")*/;
if (header.startswith(llvm::StringRef(annoTag, lenAnnoTag)))
sema.Diags.Report(l, id) << name << header.drop_front(lenAnnoTag);
}
bool AutoloadCallback::LookupObject (TagDecl *t) {
if (m_ShowSuggestions && t->hasAttr<AnnotateAttr>())
report(t->getLocation(),t->getNameAsString(),t->getAttr<AnnotateAttr>()->getAnnotation());
return false;
}
class AutoloadingVisitor: public RecursiveASTVisitor<AutoloadingVisitor> {
2014-08-06 00:07:20 +02:00
private:
2015-02-05 10:24:50 -06:00
///\brief Flag determining the visitor's actions. If true, register autoload
/// entries, i.e. remember the connection between filename and the declaration
2015-02-05 15:32:42 +01:00
/// that needs to be updated on #include of the filename.
/// If false, react on an #include by adjusting the forward decls, e.g. by
/// removing the default tremplate arguments (that will now be provided by
/// the definition read from the include) and by removing enum declarations
/// that would otherwise be duplicates.
2014-08-06 00:07:20 +02:00
bool m_IsStoringState;
AutoloadCallback::FwdDeclsMap* m_Map;
clang::Preprocessor* m_PP;
clang::Sema* m_Sema;
const clang::FileEntry* m_PrevFE;
std::string m_PrevFileName;
2014-08-06 00:07:20 +02:00
private:
void InsertIntoAutoloadingState (Decl* decl, llvm::StringRef annotation) {
assert(!annotation.empty() && "Empty annotation!");
if (!annotation.startswith(llvm::StringRef(annoTag, lenAnnoTag))) {
// not an autoload annotation.
return;
}
2014-08-06 00:07:20 +02:00
assert(m_PP);
const FileEntry* FE = 0;
SourceLocation fileNameLoc;
bool isAngled = false;
2015-01-15 15:09:46 +01:00
const DirectoryLookup* FromDir = 0;
const FileEntry* FromFile = 0;
2014-08-06 00:07:20 +02:00
const DirectoryLookup* CurDir = 0;
llvm::StringRef FileName = annotation.drop_front(lenAnnoTag);
if (FileName.equals(m_PrevFileName))
FE = m_PrevFE;
else {
FE = m_PP->LookupFile(fileNameLoc, FileName, isAngled,
2015-01-15 15:09:46 +01:00
FromDir, FromFile, CurDir, /*SearchPath*/0,
/*RelativePath*/ 0, /*suggestedModule*/0,
/*SkipCache*/ false, /*OpenFile*/ false,
/*CacheFail*/ true);
m_PrevFE = FE;
m_PrevFileName = FileName;
}
2014-08-06 00:07:20 +02:00
if (FE) {
auto& Vec = (*m_Map)[FE];
Vec.push_back(decl);
} else {
llvm::errs()
<< "Error in cling::AutoloadingVisitor::InsertIntoAutoloadingState:\n"
" Missing FileEntry for " << FileName << "\n";
if (NamedDecl* ND = dyn_cast<NamedDecl>(decl)) {
llvm::errs() << " requested to autoload type ";
ND->getNameForDiagnostic(llvm::errs(),
ND->getASTContext().getPrintingPolicy(),
true /*qualified*/);
llvm::errs() << "\n";
}
}
2014-08-06 00:07:20 +02:00
}
public:
AutoloadingVisitor():
m_IsStoringState(false), m_Map(0), m_PP(0), m_Sema(0), m_PrevFE(0) {}
void RemoveDefaultArgsOf(Decl* D, Sema* S) {
//D = D->getMostRecentDecl();
m_Sema = S;
TraverseDecl(D);
//while ((D = D->getPreviousDecl()))
// TraverseDecl(D);
}
2014-08-06 00:07:20 +02:00
void TrackDefaultArgStateOf(Decl* D, AutoloadCallback::FwdDeclsMap& map,
Preprocessor& PP) {
m_IsStoringState = true;
m_Map = &map;
m_PP = &PP;
TraverseDecl(D);
m_PP = 0;
m_Map = 0;
m_IsStoringState = false;
}
bool shouldVisitTemplateInstantiations() { return true; }
bool VisitDecl(Decl* D) {
if (!m_IsStoringState)
return true;
if (!D->hasAttr<AnnotateAttr>())
2014-08-13 14:05:59 +02:00
return true;
if (AnnotateAttr* attr = D->getAttr<AnnotateAttr>())
InsertIntoAutoloadingState(D, attr->getAnnotation());
return true;
}
bool VisitCXXRecordDecl(CXXRecordDecl* D) {
if (!D->hasAttr<AnnotateAttr>())
return true;
if (ClassTemplateDecl* TmplD = D->getDescribedClassTemplate())
return VisitTemplateDecl(TmplD);
return true;
}
bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl* D) {
if (m_IsStoringState)
return true;
if (D->hasDefaultArgument())
D->removeDefaultArgument();
return true;
}
bool VisitTemplateDecl(TemplateDecl* D) {
if (D->getTemplatedDecl() &&
!D->getTemplatedDecl()->hasAttr<AnnotateAttr>())
return true;
// If we have a definition we might be about to re-#include the
// same header containing definition that was #included previously,
// i.e. we might have multiple fwd decls for the same template.
// DO NOT remove the defaults here; the definition needs to keep it.
// (ROOT-7037)
if (ClassTemplateDecl* CTD = dyn_cast<ClassTemplateDecl>(D))
if (CXXRecordDecl* TemplatedD = CTD->getTemplatedDecl())
if (TemplatedD->getDefinition())
return true;
2014-08-08 12:45:02 +02:00
for(auto P: D->getTemplateParameters()->asArray())
TraverseDecl(P);
return true;
}
bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl* D) {
if (m_IsStoringState)
return true;
if (D->hasDefaultArgument())
D->removeDefaultArgument();
return true;
}
bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl* D) {
if (m_IsStoringState)
return true;
if (D->hasDefaultArgument())
D->removeDefaultArgument();
return true;
}
bool VisitParmVarDecl(ParmVarDecl* D) {
if (m_IsStoringState)
return true;
if (D->hasDefaultArg())
D->setDefaultArg(nullptr);
return true;
}
bool VisitEnumDecl(EnumDecl* D) {
if (m_IsStoringState)
return true;
// Now that we will read the full enum, unload the forward decl.
UnloadDecl(m_Sema, D);
return true;
}
};
void AutoloadCallback::InclusionDirective(clang::SourceLocation HashLoc,
const clang::Token &IncludeTok,
llvm::StringRef FileName,
bool IsAngled,
clang::CharSourceRange FilenameRange,
const clang::FileEntry *File,
llvm::StringRef SearchPath,
llvm::StringRef RelativePath,
const clang::Module *Imported) {
// If File is 0 this means that the #included file doesn't exist.
if (!File)
return;
2014-08-06 14:56:28 +02:00
auto found = m_Map.find(File);
if (found == m_Map.end())
return; // nothing to do, file not referred in any annotation
AutoloadingVisitor defaultArgsCleaner;
2014-08-06 14:56:28 +02:00
for (auto D : found->second) {
defaultArgsCleaner.RemoveDefaultArgsOf(D, &getInterpreter()->getSema());
}
2014-08-06 00:07:20 +02:00
// Don't need to keep track of cleaned up decls from file.
2014-08-06 14:56:28 +02:00
m_Map.erase(found);
}
AutoloadCallback::~AutoloadCallback() {
}
2014-08-06 00:07:20 +02:00
void AutoloadCallback::TransactionCommitted(const Transaction &T) {
if (T.decls_begin() == T.decls_end())
return;
if (T.decls_begin()->m_DGR.isNull())
return;
2015-06-22 15:18:05 +02:00
// The first decl must be
// extern int __Cling_Autoloading_Map;
bool HaveAutoloadingMapMarker = false;
for (auto I = T.decls_begin(), E = T.decls_end();
!HaveAutoloadingMapMarker && I != E; ++I) {
if (I->m_Call != cling::Transaction::kCCIHandleTopLevelDecl)
return;
for (auto&& D: I->m_DGR) {
if (isa<EmptyDecl>(D))
continue;
else if (auto VD = dyn_cast<VarDecl>(D)) {
HaveAutoloadingMapMarker
= VD->hasExternalStorage() && VD->getIdentifier()
&& VD->getName().equals("__Cling_Autoloading_Map");
if (!HaveAutoloadingMapMarker)
return;
break;
} else
return;
}
2015-06-22 15:18:05 +02:00
}
if (!HaveAutoloadingMapMarker)
return;
AutoloadingVisitor defaultArgsStateCollector;
Preprocessor& PP = m_Interpreter->getCI()->getPreprocessor();
for (auto I = T.decls_begin(), E = T.decls_end(); I != E; ++I)
for (auto&& D: I->m_DGR)
defaultArgsStateCollector.TrackDefaultArgStateOf(D, m_Map, PP);
}
2014-08-05 21:48:45 +02:00
} //end namespace cling