/////////////////////////////////////////////////////////////////////////////// // // // CompilationResult.h // // Copyright (C) Microsoft Corporation. All rights reserved. // // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // // This file provides a class to parse a translation unit and return the // // diagnostic results and AST tree. // // // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include "dxc/Support/WinIncludes.h" #include "dxc/dxcapi.h" #include "dxc/dxcisense.h" #include "dxc/Support/dxcapi.use.h" #include "llvm/Support/Atomic.h" inline HRESULT IFE(HRESULT hr) { if (FAILED(hr)) { throw std::runtime_error("COM call failed"); } return hr; } inline HRESULT GetFirstChildFromCursor(IDxcCursor *cursor, IDxcCursor **pResult) { HRESULT hr; IDxcCursor **children = nullptr; unsigned childrenCount; hr = cursor->GetChildren(0, 1, &childrenCount, &children); if (SUCCEEDED(hr) && childrenCount == 1) { *pResult = children[0]; } else { *pResult = nullptr; hr = E_FAIL; } CoTaskMemFree(children); return hr; } class TrivialDxcUnsavedFile : IDxcUnsavedFile { private: volatile std::atomic m_dwRef; LPCSTR m_fileName; LPCSTR m_contents; unsigned m_length; public: TrivialDxcUnsavedFile(LPCSTR fileName, LPCSTR contents) : m_dwRef(0), m_fileName(fileName), m_contents(contents) { m_length = strlen(m_contents); } static HRESULT Create(LPCSTR fileName, LPCSTR contents, IDxcUnsavedFile** pResult) { CComPtr pNewValue = new TrivialDxcUnsavedFile(fileName, contents); return pNewValue.QueryInterface(pResult); } ULONG STDMETHODCALLTYPE AddRef() { return (ULONG)++m_dwRef; } ULONG STDMETHODCALLTYPE Release() { ULONG result = (ULONG)--m_dwRef; if (result == 0) delete this; return result; } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; if (IsEqualIID(iid, __uuidof(IUnknown)) || IsEqualIID(iid, __uuidof(INoMarshal)) || IsEqualIID(iid, __uuidof(IDxcUnsavedFile))) { *ppvObject = reinterpret_cast(this); reinterpret_cast(this)->AddRef(); return S_OK; } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE GetFileName(LPSTR* pFileName) { *pFileName = (LPSTR)CoTaskMemAlloc(1 + strlen(m_fileName)); strcpy(*pFileName, m_fileName); return S_OK; } HRESULT STDMETHODCALLTYPE GetContents(LPSTR* pContents) { *pContents = (LPSTR)CoTaskMemAlloc(m_length + 1); memcpy(*pContents, m_contents, m_length + 1); return S_OK; } HRESULT STDMETHODCALLTYPE GetLength(unsigned* pLength) { *pLength = m_length; return S_OK; } }; class HlslIntellisenseSupport : public dxc::DxcDllSupport { public: HlslIntellisenseSupport() {} HlslIntellisenseSupport(HlslIntellisenseSupport &&other) : dxc::DxcDllSupport(std::move(other)) {} HRESULT CreateIntellisense(_Outptr_ IDxcIntelliSense **pResult) { return CreateInstance(CLSID_DxcIntelliSense, pResult); } }; /// Summary of the results of a clang compilation operation. class CompilationResult { private: // Keep Intellisense alive. std::shared_ptr IsenseSupport; /// The top-level Intellisense object. CComPtr Intellisense; /// The libclang index for the compilation operation. CComPtr Index; /// The number of diagnostic messages emitted. unsigned NumDiagnostics; /// The number of diagnostic messages emitted that indicate errors. unsigned NumErrorDiagnostics; /// The diagnostic messages emitted. std::vector DiagnosticMessages; /// The severity of diagnostic messages. std::vector DiagnosticSeverities; // Hide the copy constructor. CompilationResult(const CompilationResult&); public: CompilationResult(std::shared_ptr support, IDxcIntelliSense* isense, IDxcIndex* index, IDxcTranslationUnit* tu) : IsenseSupport(support), Intellisense(isense), Index(index), NumErrorDiagnostics(0), TU(tu) { if (tu) { IFE(tu->GetNumDiagnostics(&NumDiagnostics)); } else { NumDiagnostics = 0; } DxcDiagnosticDisplayOptions diagnosticOptions; IFE(isense->GetDefaultDiagnosticDisplayOptions(&diagnosticOptions)); for (unsigned i = 0; i < NumDiagnostics; i++) { CComPtr diagnostic; IFE(tu->GetDiagnostic(i, &diagnostic)); LPSTR format; IFE(diagnostic->FormatDiagnostic(diagnosticOptions, &format)); DiagnosticMessages.push_back(std::string(format)); CoTaskMemFree(format); DxcDiagnosticSeverity severity; IFE(diagnostic->GetSeverity(&severity)); DiagnosticSeverities.push_back(severity); if (IsErrorSeverity(severity)) { NumErrorDiagnostics++; } } assert(NumErrorDiagnostics <= NumDiagnostics && "else counting code in loop is incorrect"); assert(DiagnosticMessages.size() == NumDiagnostics && "else some diagnostics have no message"); assert(DiagnosticSeverities.size() == NumDiagnostics && "else some diagnostics have no severity"); } CompilationResult(CompilationResult&& other) : IsenseSupport(std::move(other.IsenseSupport)) { // Allow move constructor. Intellisense = other.Intellisense; other.Intellisense = nullptr; Index = other.Index; other.Index = nullptr; TU = other.TU; other.TU = nullptr; NumDiagnostics = other.NumDiagnostics; NumErrorDiagnostics = other.NumErrorDiagnostics; DiagnosticMessages = std::move(other.DiagnosticMessages); DiagnosticSeverities = std::move(other.DiagnosticSeverities); } ~CompilationResult() { Dispose(); } /// The translation unit resulting from the compilation. CComPtr TU; static const char *getDefaultFileName() { return "filename.hlsl"; } static std::shared_ptr DefaultHlslSupport; static std::shared_ptr GetHlslSupport() { if (DefaultHlslSupport.get() != nullptr) { return DefaultHlslSupport; } std::shared_ptr result = std::make_shared(); IFE(result->Initialize()); return result; } static CompilationResult CreateForProgramAndArgs(const char* text, size_t textLen, _In_count_(commandLineArgsCount) const char* commandLineArgs[], unsigned commandLineArgsCount, _In_opt_ DxcTranslationUnitFlags* options = nullptr) { std::shared_ptr support(GetHlslSupport()); CComPtr isense; IFE(support->CreateIntellisense(&isense)); CComPtr tuIndex; CComPtr tu; const char *fileName = getDefaultFileName(); if (textLen == 0) textLen = strlen(text); IFE(isense->CreateIndex(&tuIndex)); CComPtr unsavedFile; IFE(TrivialDxcUnsavedFile::Create(fileName, text, &unsavedFile)); DxcTranslationUnitFlags localOptions; if (options == nullptr) { IFE(isense->GetDefaultEditingTUOptions(&localOptions)); } else { localOptions = *options; } IFE(tuIndex->ParseTranslationUnit(fileName, commandLineArgs, commandLineArgsCount, &(unsavedFile.p), 1, localOptions, &tu)); return CompilationResult(support, isense.p, tuIndex.p, tu.p); } static CompilationResult CreateForProgram(const char *text, size_t textLen, _In_opt_ DxcTranslationUnitFlags *options = nullptr) { const char *commandLineArgs[] = {"-c", "-ferror-limit=200"}; unsigned commandLineArgsCount = _countof(commandLineArgs); return CreateForProgramAndArgs(text, textLen, commandLineArgs, commandLineArgsCount, options); } static CompilationResult CreateForCommandLine(char* arguments, const char* fileName) { std::shared_ptr support(GetHlslSupport()); return CreateForCommandLine(arguments, fileName, support); } static CompilationResult CreateForCommandLine(char* arguments, const char* fileName, std::shared_ptr support) { CComPtr isense; IFE(support->CreateIntellisense(&isense)); CComPtr tuIndex; CComPtr tu; const char* commandLineArgs[32]; unsigned commandLineArgsCount = 0; char* nextArg = arguments; // Set a very high number of errors to avoid giving up too early. commandLineArgs[commandLineArgsCount++] = "-ferror-limit=2000"; // Turn on spell checking for compatibility. This produces error messages // that suggest corrected spellings. commandLineArgs[commandLineArgsCount++] = "-fspell-checking"; // Turn off color diagnostics to avoid control characters in diagnostic // stream. commandLineArgs[commandLineArgsCount++] = "-fno-color-diagnostics"; IFE(isense->CreateIndex(&tuIndex)); // Split command line arguments by spaces if (nextArg) { // skip leading spaces while (*nextArg == ' ') nextArg++; commandLineArgs[commandLineArgsCount++] = nextArg; while ((*nextArg != '\0')) { if (*nextArg == ' ') { *nextArg = 0; commandLineArgs[commandLineArgsCount++] = nextArg + 1; } nextArg++; } } DxcTranslationUnitFlags options; IFE(isense->GetDefaultEditingTUOptions(&options)); IFE(tuIndex->ParseTranslationUnit(fileName, commandLineArgs, commandLineArgsCount, nullptr, 0, options, &tu)); return CompilationResult(support, isense.p, tuIndex.p, tu.p); } bool IsTUAvailable() const { return TU != nullptr; } bool ParseSucceeded() const { return TU != nullptr && NumErrorDiagnostics == 0; } static bool IsErrorSeverity(DxcDiagnosticSeverity severity) { return (severity == DxcDiagnostic_Error || severity == DxcDiagnostic_Fatal); } /// Gets a string with all error messages concatenated, one per line. std::string GetTextForErrors() const { assert(DiagnosticMessages.size() == NumDiagnostics && "otherwise some diagnostics have no message"); assert(DiagnosticSeverities.size() == NumDiagnostics && "otherwise some diagnostics have no severity"); std::stringstream ostr; for (size_t i = 0; i < DiagnosticMessages.size(); i++) { if (IsErrorSeverity(DiagnosticSeverities[i])) { ostr << DiagnosticMessages[i] << '\n'; } } return ostr.str(); } /// Releases resources, including the translation unit AST. void Dispose() { TU = nullptr; Index = nullptr; Intellisense = nullptr; IsenseSupport = nullptr; } private: #if SUPPORTS_CURSOR_WALK struct CursorStringData { std::stringstream &ostr; std::vector cursors; CursorStringData(std::stringstream &the_ostr) : ostr(the_ostr) {} }; static CXChildVisitResult AppendCursorStringCallback( CXCursor cursor, CXCursor parent, CXClientData client_data) { CursorStringData* d = (CursorStringData*)client_data; auto cursorsStart = std::begin(d->cursors); auto cursorsEnd = std::end(d->cursors); auto parentLocation = std::find(cursorsStart, cursorsEnd, parent); assert(parentLocation != cursorsEnd && "otherwise the parent was not visited previously"); AppendCursorString(cursor, parentLocation - cursorsStart, d->ostr); d->cursors.resize(1 + (parentLocation - cursorsStart)); d->cursors.push_back(cursor); return CXChildVisit_Recurse; } #endif static void AppendCursorString(IDxcCursor* cursor, size_t indent, std::stringstream& ostr) { if (indent > 0) { std::streamsize prior = ostr.width(); ostr.width(indent); ostr << ' '; ostr.width(prior); } CComPtr type; DxcCursorKind kind; cursor->GetKind(&kind); cursor->GetCursorType(&type); switch (kind) { case DxcCursor_UnexposedDecl: ostr << "UnexposedDecl"; break; case DxcCursor_StructDecl: ostr << "StructDecl"; break; case DxcCursor_UnionDecl: ostr << "UnionDecl"; break; case DxcCursor_ClassDecl: ostr << "ClassDecl"; break; case DxcCursor_EnumDecl: ostr << "EnumDecl"; break; case DxcCursor_FieldDecl: ostr << "FieldDecl"; break; case DxcCursor_EnumConstantDecl: ostr << "EnumConstantDecl"; break; case DxcCursor_FunctionDecl: ostr << "FunctionDecl"; break; case DxcCursor_VarDecl: ostr << "VarDecl"; break; case DxcCursor_ParmDecl: ostr << "ParmDecl"; break; case DxcCursor_ObjCInterfaceDecl: ostr << "ObjCInterfaceDecl"; break; case DxcCursor_ObjCCategoryDecl: ostr << "ObjCCategoryDecl"; break; case DxcCursor_ObjCProtocolDecl: ostr << "ObjCProtocolDecl"; break; case DxcCursor_ObjCPropertyDecl: ostr << "ObjCPropertyDecl"; break; case DxcCursor_ObjCIvarDecl: ostr << "ObjCIvarDecl"; break; case DxcCursor_ObjCInstanceMethodDecl: ostr << "ObjCInstanceMethodDecl"; break; case DxcCursor_ObjCClassMethodDecl: ostr << "ObjCClassMethodDecl"; break; case DxcCursor_ObjCImplementationDecl: ostr << "ObjCImplementationDecl"; break; case DxcCursor_ObjCCategoryImplDecl: ostr << "ObjCCategoryImplDecl"; break; case DxcCursor_TypedefDecl: ostr << "TypedefDecl"; break; case DxcCursor_CXXMethod: ostr << "CXXMethod"; break; case DxcCursor_Namespace: ostr << "Namespace"; break; case DxcCursor_LinkageSpec: ostr << "LinkageSpec"; break; case DxcCursor_Constructor: ostr << "Constructor"; break; case DxcCursor_Destructor: ostr << "Destructor"; break; case DxcCursor_ConversionFunction: ostr << "ConversionFunction"; break; case DxcCursor_TemplateTypeParameter: ostr << "TemplateTypeParameter"; break; case DxcCursor_NonTypeTemplateParameter: ostr << "NonTypeTemplateParameter"; break; case DxcCursor_TemplateTemplateParameter: ostr << "TemplateTemplateParameter"; break; case DxcCursor_FunctionTemplate: ostr << "FunctionTemplate"; break; case DxcCursor_ClassTemplate: ostr << "ClassTemplate"; break; case DxcCursor_ClassTemplatePartialSpecialization: ostr << "ClassTemplatePartialSpecialization"; break; case DxcCursor_NamespaceAlias: ostr << "NamespaceAlias"; break; case DxcCursor_UsingDirective: ostr << "UsingDirective"; break; case DxcCursor_UsingDeclaration: ostr << "UsingDeclaration"; break; case DxcCursor_TypeAliasDecl: ostr << "TypeAliasDecl"; break; case DxcCursor_ObjCSynthesizeDecl: ostr << "ObjCSynthesizeDecl"; break; case DxcCursor_ObjCDynamicDecl: ostr << "ObjCDynamicDecl"; break; case DxcCursor_CXXAccessSpecifier: ostr << "CXXAccessSpecifier"; break; case DxcCursor_ObjCSuperClassRef: ostr << "ObjCSuperClassRef"; break; case DxcCursor_ObjCProtocolRef: ostr << "ObjCProtocolRef"; break; case DxcCursor_ObjCClassRef: ostr << "ObjCClassRef"; break; case DxcCursor_TypeRef: ostr << "TypeRef"; break; case DxcCursor_CXXBaseSpecifier: ostr << "CXXBaseSpecifier"; break; case DxcCursor_TemplateRef: ostr << "TemplateRef"; break; case DxcCursor_NamespaceRef: ostr << "NamespaceRef"; break; case DxcCursor_MemberRef: ostr << "MemberRef"; break; case DxcCursor_LabelRef: ostr << "LabelRef"; break; case DxcCursor_OverloadedDeclRef: ostr << "OverloadedDeclRef"; break; case DxcCursor_VariableRef: ostr << "VariableRef"; break; case DxcCursor_InvalidFile: ostr << "InvalidFile"; break; case DxcCursor_NoDeclFound: ostr << "NoDeclFound"; break; case DxcCursor_NotImplemented: ostr << "NotImplemented"; break; case DxcCursor_InvalidCode: ostr << "InvalidCode"; break; case DxcCursor_UnexposedExpr: ostr << "UnexposedExpr"; break; case DxcCursor_DeclRefExpr: ostr << "DeclRefExpr"; break; case DxcCursor_MemberRefExpr: ostr << "MemberRefExpr"; break; case DxcCursor_CallExpr: ostr << "CallExpr"; break; case DxcCursor_ObjCMessageExpr: ostr << "ObjCMessageExpr"; break; case DxcCursor_BlockExpr: ostr << "BlockExpr"; break; case DxcCursor_IntegerLiteral: ostr << "IntegerLiteral"; break; case DxcCursor_FloatingLiteral: ostr << "FloatingLiteral"; break; case DxcCursor_ImaginaryLiteral: ostr << "ImaginaryLiteral"; break; case DxcCursor_StringLiteral: ostr << "StringLiteral"; break; case DxcCursor_CharacterLiteral: ostr << "CharacterLiteral"; break; case DxcCursor_ParenExpr: ostr << "ParenExpr"; break; case DxcCursor_UnaryOperator: ostr << "UnaryOperator"; break; case DxcCursor_ArraySubscriptExpr: ostr << "ArraySubscriptExpr"; break; case DxcCursor_BinaryOperator: ostr << "BinaryOperator"; break; case DxcCursor_CompoundAssignOperator: ostr << "CompoundAssignOperator"; break; case DxcCursor_ConditionalOperator: ostr << "ConditionalOperator"; break; case DxcCursor_CStyleCastExpr: ostr << "CStyleCastExpr"; break; case DxcCursor_CompoundLiteralExpr: ostr << "CompoundLiteralExpr"; break; case DxcCursor_InitListExpr: ostr << "InitListExpr"; break; case DxcCursor_AddrLabelExpr: ostr << "AddrLabelExpr"; break; case DxcCursor_StmtExpr: ostr << "StmtExpr"; break; case DxcCursor_GenericSelectionExpr: ostr << "GenericSelectionExpr"; break; case DxcCursor_GNUNullExpr: ostr << "GNUNullExpr"; break; case DxcCursor_CXXStaticCastExpr: ostr << "CXXStaticCastExpr"; break; case DxcCursor_CXXDynamicCastExpr: ostr << "CXXDynamicCastExpr"; break; case DxcCursor_CXXReinterpretCastExpr: ostr << "CXXReinterpretCastExpr"; break; case DxcCursor_CXXConstCastExpr: ostr << "CXXConstCastExpr"; break; case DxcCursor_CXXFunctionalCastExpr: ostr << "CXXFunctionalCastExpr"; break; case DxcCursor_CXXTypeidExpr: ostr << "CXXTypeidExpr"; break; case DxcCursor_CXXBoolLiteralExpr: ostr << "CXXBoolLiteralExpr"; break; case DxcCursor_CXXNullPtrLiteralExpr: ostr << "CXXNullPtrLiteralExpr"; break; case DxcCursor_CXXThisExpr: ostr << "CXXThisExpr"; break; case DxcCursor_CXXThrowExpr: ostr << "CXXThrowExpr"; break; case DxcCursor_CXXNewExpr: ostr << "CXXNewExpr"; break; case DxcCursor_CXXDeleteExpr: ostr << "CXXDeleteExpr"; break; case DxcCursor_UnaryExpr: ostr << "UnaryExpr"; break; case DxcCursor_ObjCStringLiteral: ostr << "ObjCStringLiteral"; break; case DxcCursor_ObjCEncodeExpr: ostr << "ObjCEncodeExpr"; break; case DxcCursor_ObjCSelectorExpr: ostr << "ObjCSelectorExpr"; break; case DxcCursor_ObjCProtocolExpr: ostr << "ObjCProtocolExpr"; break; case DxcCursor_ObjCBridgedCastExpr: ostr << "ObjCBridgedCastExpr"; break; case DxcCursor_PackExpansionExpr: ostr << "PackExpansionExpr"; break; case DxcCursor_SizeOfPackExpr: ostr << "SizeOfPackExpr"; break; case DxcCursor_LambdaExpr: ostr << "LambdaExpr"; break; case DxcCursor_ObjCBoolLiteralExpr: ostr << "ObjCBoolLiteralExpr"; break; case DxcCursor_ObjCSelfExpr: ostr << "ObjCSelfExpr"; break; case DxcCursor_UnexposedStmt: ostr << "UnexposedStmt"; break; case DxcCursor_LabelStmt: ostr << "LabelStmt"; break; case DxcCursor_CompoundStmt: ostr << "CompoundStmt"; break; case DxcCursor_CaseStmt: ostr << "CaseStmt"; break; case DxcCursor_DefaultStmt: ostr << "DefaultStmt"; break; case DxcCursor_IfStmt: ostr << "IfStmt"; break; case DxcCursor_SwitchStmt: ostr << "SwitchStmt"; break; case DxcCursor_WhileStmt: ostr << "WhileStmt"; break; case DxcCursor_DoStmt: ostr << "DoStmt"; break; case DxcCursor_ForStmt: ostr << "ForStmt"; break; case DxcCursor_GotoStmt: ostr << "GotoStmt"; break; case DxcCursor_IndirectGotoStmt: ostr << "IndirectGotoStmt"; break; case DxcCursor_ContinueStmt: ostr << "ContinueStmt"; break; case DxcCursor_BreakStmt: ostr << "BreakStmt"; break; case DxcCursor_ReturnStmt: ostr << "ReturnStmt"; break; case DxcCursor_GCCAsmStmt: ostr << "GCCAsmStmt"; break; case DxcCursor_ObjCAtTryStmt: ostr << "ObjCAtTryStmt"; break; case DxcCursor_ObjCAtCatchStmt: ostr << "ObjCAtCatchStmt"; break; case DxcCursor_ObjCAtFinallyStmt: ostr << "ObjCAtFinallyStmt"; break; case DxcCursor_ObjCAtThrowStmt: ostr << "ObjCAtThrowStmt"; break; case DxcCursor_ObjCAtSynchronizedStmt: ostr << "ObjCAtSynchronizedStmt"; break; case DxcCursor_ObjCAutoreleasePoolStmt: ostr << "ObjCAutoreleasePoolStmt"; break; case DxcCursor_ObjCForCollectionStmt: ostr << "ObjCForCollectionStmt"; break; case DxcCursor_CXXCatchStmt: ostr << "CXXCatchStmt"; break; case DxcCursor_CXXTryStmt: ostr << "CXXTryStmt"; break; case DxcCursor_CXXForRangeStmt: ostr << "CXXForRangeStmt"; break; case DxcCursor_SEHTryStmt: ostr << "SEHTryStmt"; break; case DxcCursor_SEHExceptStmt: ostr << "SEHExceptStmt"; break; case DxcCursor_SEHFinallyStmt: ostr << "SEHFinallyStmt"; break; case DxcCursor_MSAsmStmt: ostr << "MSAsmStmt"; break; case DxcCursor_NullStmt: ostr << "NullStmt"; break; case DxcCursor_DeclStmt: ostr << "DeclStmt"; break; case DxcCursor_OMPParallelDirective: ostr << "OMPParallelDirective"; break; case DxcCursor_TranslationUnit: ostr << "TranslationUnit"; break; case DxcCursor_UnexposedAttr: ostr << "UnexposedAttr"; break; #if 0 CXCursor_IBActionAttr = 401, CXCursor_IBOutletAttr = 402, CXCursor_IBOutletCollectionAttr = 403, CXCursor_CXXFinalAttr = 404, CXCursor_CXXOverrideAttr = 405, CXCursor_AnnotateAttr = 406, CXCursor_AsmLabelAttr = 407, CXCursor_PackedAttr = 408, /* Preprocessing */ CXCursor_PreprocessingDirective = 500, CXCursor_MacroDefinition = 501, CXCursor_MacroExpansion = 502, CXCursor_InclusionDirective = 503, case CXCursor_FunctionDecl: break; case CXCursor_ClassDecl: ostr << "class decl"; break; case CXCursor_TranslationUnit: ostr << "translation unit"; break; #endif default: ostr << "unknown/unhandled cursor kind " << kind; break; } DxcTypeKind typeKind; if (type != nullptr && SUCCEEDED(type->GetKind(&typeKind)) && typeKind != DxcTypeKind_Invalid) { LPSTR name; type->GetSpelling(&name); ostr << " [type " << name << "]"; CoTaskMemFree(name); } ostr << '\n'; // Recurse. IDxcCursor** children = nullptr; unsigned childrenCount; cursor->GetChildren(0, 64, &childrenCount, &children); for (unsigned i = 0; i < childrenCount; i++) { AppendCursorString(children[i], indent + 1, ostr); children[i]->Release(); } CoTaskMemFree(children); } public: std::string BuildASTString() { if (TU == nullptr) { return ""; } CComPtr cursor; std::stringstream ostr; this->TU->GetCursor(&cursor); AppendCursorString(cursor, 0, ostr); return ostr.str(); } };