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.
//------------------------------------------------------------------------------
2014-07-12 23:53:55 +05:30
# include "llvm/ADT/SmallVector.h"
# include "llvm/Support/Path.h"
2015-08-11 15:45:40 +02:00
# include "llvm/Support/raw_ostream.h"
2014-07-12 23:53:55 +05:30
# include "clang/Sema/Sema.h"
# include "clang/Basic/Diagnostic.h"
# include "clang/AST/AST.h"
2014-08-21 12:21:41 +02:00
# include "clang/AST/ASTContext.h" // for operator new[](unsigned long, ASTCtx..)
2014-08-05 21:35:25 +02:00
# include "clang/AST/RecursiveASTVisitor.h"
2014-08-19 01:08:40 +05:30
# include "clang/AST/DeclVisitor.h"
2014-07-12 23:53:55 +05:30
# include "clang/Lex/Preprocessor.h"
2014-07-13 22:13:20 +05:30
# include "clang/Frontend/CompilerInstance.h"
2014-07-12 23:53:55 +05:30
2014-06-08 22:12:58 +05:30
# include "cling/Interpreter/Interpreter.h"
# include "cling/Interpreter/InterpreterCallbacks.h"
2014-06-08 03:22:32 +05:30
# include "cling/Interpreter/AutoloadCallback.h"
2014-07-13 22:13:20 +05:30
# include "cling/Interpreter/Transaction.h"
2016-06-26 10:06:18 +02:00
# include "DeclUnloader.h"
2014-07-12 23:53:55 +05:30
2014-09-22 15:52:38 +02:00
namespace {
2014-10-02 14:38:33 +02:00
static const char annoTag [ ] = " $clingAutoload$ " ;
static const size_t lenAnnoTag = sizeof ( annoTag ) - 1 ;
2014-09-22 15:52:38 +02:00
}
2014-06-08 03:22:32 +05:30
using namespace clang ;
namespace cling {
2014-07-12 23:53:55 +05:30
2014-09-12 13:53:07 +02:00
void AutoloadCallback : : report ( clang : : SourceLocation l , llvm : : StringRef name ,
llvm : : StringRef header ) {
2014-06-08 03:22:32 +05:30
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 " ) */ ;
2014-09-22 15:52:38 +02:00
if ( header . startswith ( llvm : : StringRef ( annoTag , lenAnnoTag ) ) )
sema . Diags . Report ( l , id ) < < name < < header . drop_front ( lenAnnoTag ) ;
2014-06-08 03:22:32 +05:30
}
2014-08-05 05:42:08 +05:30
bool AutoloadCallback : : LookupObject ( TagDecl * t ) {
2014-09-19 13:59:31 +02:00
if ( m_ShowSuggestions & & t - > hasAttr < AnnotateAttr > ( ) )
2014-08-05 05:42:08 +05:30
report ( t - > getLocation ( ) , t - > getNameAsString ( ) , t - > getAttr < AnnotateAttr > ( ) - > getAnnotation ( ) ) ;
return false ;
}
2014-08-19 01:08:40 +05:30
2014-08-21 00:18:23 +05:30
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 ;
2015-06-19 16:26:08 +02:00
clang : : Sema * m_Sema ;
2014-10-02 16:26:08 +02:00
const clang : : FileEntry * m_PrevFE ;
2014-10-02 14:20:51 +02:00
std : : string m_PrevFileName ;
2014-08-06 00:07:20 +02:00
private :
2014-09-12 13:57:19 +02:00
void InsertIntoAutoloadingState ( Decl * decl , llvm : : StringRef annotation ) {
assert ( ! annotation . empty ( ) & & " Empty annotation! " ) ;
2014-09-22 15:52:38 +02:00
if ( ! annotation . startswith ( llvm : : StringRef ( annoTag , lenAnnoTag ) ) ) {
2014-09-12 13:57:19 +02:00
// 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 ;
2014-10-02 16:26:08 +02:00
llvm : : StringRef FileName = annotation . drop_front ( lenAnnoTag ) ;
if ( FileName . equals ( m_PrevFileName ) )
2014-10-02 14:20:51 +02:00
FE = m_PrevFE ;
2014-10-02 16:26:08 +02:00
else {
FE = m_PP - > LookupFile ( fileNameLoc , FileName , isAngled ,
2015-01-15 15:09:46 +01:00
FromDir , FromFile , CurDir , /*SearchPath*/ 0 ,
2014-10-02 14:20:51 +02:00
/*RelativePath*/ 0 , /*suggestedModule*/ 0 ,
2014-10-03 14:28:31 +02:00
/*SkipCache*/ false , /*OpenFile*/ false ,
/*CacheFail*/ true ) ;
2014-10-02 16:26:08 +02:00
m_PrevFE = FE ;
m_PrevFileName = FileName ;
}
2014-08-06 00:07:20 +02:00
2014-10-02 16:26:08 +02:00
if ( FE ) {
auto & Vec = ( * m_Map ) [ FE ] ;
Vec . push_back ( decl ) ;
2015-08-11 15:45:40 +02:00
} 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-10-02 16:26:08 +02:00
}
2014-08-06 00:07:20 +02:00
}
2014-08-05 21:35:25 +02:00
public :
2014-10-02 14:20:51 +02:00
AutoloadingVisitor ( ) :
2015-06-19 16:26:08 +02:00
m_IsStoringState ( false ) , m_Map ( 0 ) , m_PP ( 0 ) , m_Sema ( 0 ) , m_PrevFE ( 0 ) { }
void RemoveDefaultArgsOf ( Decl * D , Sema * S ) {
2014-08-06 01:39:34 +02:00
//D = D->getMostRecentDecl();
2015-06-19 16:26:08 +02:00
m_Sema = S ;
2014-08-05 21:35:25 +02:00
TraverseDecl ( D ) ;
2014-08-06 01:39:34 +02:00
//while ((D = D->getPreviousDecl()))
// TraverseDecl(D);
2014-07-12 23:53:55 +05:30
}
2014-08-05 21:35:25 +02:00
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 ;
}
2014-08-06 01:39:34 +02:00
bool shouldVisitTemplateInstantiations ( ) { return true ; }
2014-08-05 21:35:25 +02:00
2014-08-06 01:39:34 +02:00
bool VisitDecl ( Decl * D ) {
2014-08-13 14:05:29 +02:00
if ( ! m_IsStoringState )
2014-08-06 12:52:20 +02:00
return true ;
2014-08-08 12:44:32 +02:00
if ( ! D - > hasAttr < AnnotateAttr > ( ) )
2014-08-13 14:05:59 +02:00
return true ;
2014-08-08 12:44:32 +02:00
2015-06-19 16:26:08 +02:00
if ( AnnotateAttr * attr = D - > getAttr < AnnotateAttr > ( ) )
2014-08-06 12:52:20 +02:00
InsertIntoAutoloadingState ( D , attr - > getAnnotation ( ) ) ;
2014-08-06 01:39:34 +02:00
return true ;
}
2014-08-13 14:06:58 +02:00
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 ) {
2014-08-13 17:02:54 +02:00
if ( D - > getTemplatedDecl ( ) & &
! D - > getTemplatedDecl ( ) - > hasAttr < AnnotateAttr > ( ) )
2014-08-12 09:32:38 +02:00
return true ;
2014-08-13 14:06:58 +02:00
2015-02-05 15:33:28 +01:00
// 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 ;
}
2014-08-13 17:02:54 +02:00
bool VisitTemplateTemplateParmDecl ( TemplateTemplateParmDecl * D ) {
if ( m_IsStoringState )
return true ;
if ( D - > hasDefaultArgument ( ) )
D - > removeDefaultArgument ( ) ;
return true ;
}
2014-08-13 14:06:58 +02:00
bool VisitNonTypeTemplateParmDecl ( NonTypeTemplateParmDecl * D ) {
2014-08-12 09:32:38 +02:00
if ( m_IsStoringState )
return true ;
2014-08-05 21:35:25 +02:00
if ( D - > hasDefaultArgument ( ) )
D - > removeDefaultArgument ( ) ;
return true ;
2014-07-13 22:45:08 +05:30
}
2014-08-05 21:35:25 +02:00
2014-08-13 14:06:58 +02:00
bool VisitParmVarDecl ( ParmVarDecl * D ) {
2014-08-12 09:32:38 +02:00
if ( m_IsStoringState )
return true ;
2014-08-05 21:35:25 +02:00
if ( D - > hasDefaultArg ( ) )
D - > setDefaultArg ( nullptr ) ;
return true ;
}
2015-06-19 16:26:08 +02:00
bool VisitEnumDecl ( EnumDecl * D ) {
if ( m_IsStoringState )
return true ;
// Now that we will read the full enum, unload the forward decl.
2016-06-26 18:26:35 +03:00
UnloadDecl ( m_Sema , D ) ;
2015-06-19 16:26:08 +02:00
return true ;
}
2014-08-05 21:35:25 +02:00
} ;
2014-06-08 03:22:32 +05:30
2014-07-12 23:53:55 +05:30
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 ) {
2014-08-21 15:58:59 +02:00
// If File is 0 this means that the #included file doesn't exist.
if ( ! File )
return ;
2014-07-29 23:14:15 +02:00
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
2014-07-12 23:53:55 +05:30
2014-08-21 00:18:23 +05:30
AutoloadingVisitor defaultArgsCleaner ;
2014-08-06 14:56:28 +02:00
for ( auto D : found - > second ) {
2015-06-19 16:26:08 +02:00
defaultArgsCleaner . RemoveDefaultArgsOf ( D , & getInterpreter ( ) - > getSema ( ) ) ;
2014-07-12 23:53:55 +05:30
}
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 ) ;
2014-07-12 23:53:55 +05:30
}
2014-06-08 03:22:32 +05:30
2014-07-13 22:13:20 +05:30
AutoloadCallback : : ~ AutoloadCallback ( ) {
}
2014-08-06 00:07:20 +02:00
void AutoloadCallback : : TransactionCommitted ( const Transaction & T ) {
2014-08-08 12:45:25 +02:00
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 ;
2014-07-13 22:13:20 +05:30
}
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-07-13 22:13:20 +05:30
}
2014-08-05 21:48:45 +02:00
} //end namespace cling