//FIXME. make it less stupid static void parseInterfaces(ClassDef &Def, clang::Expr *Content, clang::Sema &Sema) { clang::Preprocessor &PP = Sema.getPreprocessor(); clang::StringLiteral *Val = llvm::dyn_cast<clang::StringLiteral>(Content); if (!Val) { PP.getDiagnostics().Report(Content->getExprLoc(), PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "Invalid Q_INTERFACES annotation")); return; } llvm::MemoryBuffer* Buf = maybe_unique(llvm::MemoryBuffer::getMemBufferCopy(Val->getString(), "Q_INTERFACES")); clang::Lexer Lex(CreateFileIDForMemBuffer(PP, Buf, Content->getExprLoc()), Buf, PP.getSourceManager(), PP.getLangOpts()); clang::Token Tok; bool Append = false; bool Error = false; while (true) { Lex.LexFromRawLexer(Tok); if (Tok.is(clang::tok::eof)) break; if (Tok.is(clang::tok::raw_identifier)) PP.LookUpIdentifierInfo(Tok); if (Tok.is(clang::tok::identifier)) { if (Append) Def.Interfaces.back() += PP.getSpelling(Tok); else Def.Interfaces.push_back(PP.getSpelling(Tok)); Append = false; continue; } if (Append) { Error = true; break; } if (Tok.is(clang::tok::coloncolon)) { Def.Interfaces.back() += PP.getSpelling(Tok); Append = true; continue; } if (!Tok.is(clang::tok::colon)) { Error = true; break; } } if (Error || Append || !Tok.is(clang::tok::eof)) { PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "parse error in Q_INTERFACES")); } // TODO: check interface validity }
static bool isInStdNamespace(clang::Sema& sema, NamespaceDecl* ns) { while (ns) { if (sema.getStdNamespace()->InEnclosingNamespaceSetOf(ns)) return true; ns = dyn_cast<NamespaceDecl>(ns->getParent()); } return false; }
void CommentHandler::handleComment(Annotator &A, Generator& generator, clang::Sema &Sema, const char *bufferStart, int commentStart, int len, clang::SourceLocation searchLocBegin, clang::SourceLocation searchLocEnd, clang::SourceLocation commentLoc) { llvm::StringRef rawString(bufferStart+commentStart, len); std::string attributes; std::string DeclRef; if ((rawString.ltrim().startswith("/**") && !rawString.ltrim().startswith("/***")) || rawString.ltrim().startswith("/*!") || rawString.ltrim().startswith("//!") || (rawString.ltrim().startswith("///") && !rawString.ltrim().startswith("////"))) #if CLANG_VERSION_MAJOR==3 && CLANG_VERSION_MINOR<=4 if (rawString.find("deprecated") == rawString.npos) // workaround crash in comments::Sema::checkDeprecatedCommand #endif { attributes = "class=\"doc\""; clang::Preprocessor &PP = Sema.getPreprocessor(); clang::comments::CommandTraits traits(PP.getPreprocessorAllocator(), clang::CommentOptions()); #if CLANG_VERSION_MAJOR==3 && CLANG_VERSION_MINOR<=4 traits.registerBlockCommand("deprecated"); // avoid typo correction leading to crash. #endif clang::comments::Lexer lexer(PP.getPreprocessorAllocator(), PP.getDiagnostics(), traits, clang::SourceLocation::getFromRawEncoding(commentStart), bufferStart + commentStart, bufferStart + commentStart + len); clang::comments::Sema sema(PP.getPreprocessorAllocator(), PP.getSourceManager(), PP.getDiagnostics(), traits, &PP); clang::comments::Parser parser(lexer, sema, PP.getPreprocessorAllocator(), PP.getSourceManager(), PP.getDiagnostics(), traits); auto fullComment = parser.parseFullComment(); CommentVisitor visitor{A, generator, traits, Sema}; visitor.visit(fullComment); DeclRef = visitor.DeclRef; } if (!DeclRef.empty()) { docs.insert({std::move(DeclRef), { rawString.str() , commentLoc }}); generator.addTag("i", attributes, commentStart, len); return; } // Try to find a matching declaration const auto &dof = decl_offsets; //is there one and one single decl in that range. auto it_before = dof.lower_bound(searchLocBegin); auto it_after = dof.upper_bound(searchLocEnd); if (it_before != dof.end() && it_after != dof.begin() && it_before == (--it_after)) { if (it_before->second.second) { docs.insert({it_before->second.first, { rawString.str() , commentLoc }}); } else { attributes %= " data-doc=\"" % it_before->second.first % "\""; } } generator.addTag("i", attributes, commentStart, len); }
static clang::CanQualType getClangBuiltinTypeFromTypedef( clang::Sema &sema, StringRef typedefName) { auto &context = sema.getASTContext(); auto identifier = &context.Idents.get(typedefName); auto found = sema.LookupSingleName(sema.TUScope, identifier, clang::SourceLocation(), clang::Sema::LookupOrdinaryName); auto typedefDecl = dyn_cast_or_null<clang::TypedefDecl>(found); if (!typedefDecl) return {}; auto underlyingTy = context.getCanonicalType(typedefDecl->getUnderlyingType()); if (underlyingTy->getAs<clang::BuiltinType>()) return underlyingTy; return {}; }
ClassDef MocNg::parseClass(clang::CXXRecordDecl* RD, clang::Sema& Sema) { clang::Preprocessor &PP = Sema.getPreprocessor(); ClassDef Def; Def.Record = RD; for (auto it = RD->decls_begin(); it != RD->decls_end(); ++it) { if (clang::StaticAssertDecl *S = llvm::dyn_cast<clang::StaticAssertDecl>(*it) ) { if (auto *E = llvm::dyn_cast<clang::UnaryExprOrTypeTraitExpr>(S->getAssertExpr())) if (clang::ParenExpr *PE = llvm::dyn_cast<clang::ParenExpr>(E->getArgumentExpr())) { llvm::StringRef key = S->getMessage()->getString(); if (key == "qt_property") { clang::StringLiteral *Val = llvm::dyn_cast<clang::StringLiteral>(PE->getSubExpr()); if (Val) { PropertyParser Parser(Val->getString(), // Val->getStrTokenLoc(0), Val->getLocationOfByte(0, PP.getSourceManager(), PP.getLangOpts(), PP.getTargetInfo()), Sema, Def.Record); Def.Properties.push_back(Parser.parseProperty()); Def.addExtra(Parser.Extra); } else { PP.getDiagnostics().Report(S->getLocation(), PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "Invalid Q_PROPERTY annotation")); } } else if (key == "qt_private_property") { clang::StringLiteral *Val1 = nullptr, *Val2 = nullptr; std::tie(Val1, Val2) = ExtractLiterals(PE, PP, "Q_PRIVATE_PROPERTY", "Invalid Q_PRIVATE_PROPERTY annotation"); if (Val1 && Val2) { PropertyParser Parser(Val2->getString(), Val2->getLocationOfByte(0, PP.getSourceManager(), PP.getLangOpts(), PP.getTargetInfo()), Sema, Def.Record); PropertyDef P = Parser.parseProperty(true); P.inPrivateClass = Val1->getString(); Def.Properties.push_back(std::move(P)); Def.addExtra(Parser.Extra); } } else if (key == "qt_private_slot") { clang::StringLiteral *Val1 = nullptr, *Val2 = nullptr; std::tie(Val1, Val2) = ExtractLiterals(PE, PP, "Q_PRIVATE_SLOT", "Invalid Q_PRIVATE_SLOT annotation"); if (Val1 && Val2) { PropertyParser Parser(Val2->getString(), Val2->getLocationOfByte(0, PP.getSourceManager(), PP.getLangOpts(), PP.getTargetInfo()), Sema, Def.Record); PrivateSlotDef P = Parser.parsePrivateSlot(); P.InPrivateClass = Val1->getString(); if (!P.Name.empty()) { Def.PrivateSlotCount += P.NumDefault + 1; Def.PrivateSlots.push_back(std::move(P)); } } } else if (key == "qt_enums") { parseEnums(Def, false, PE->getSubExpr(), Sema); } else if (key == "qt_flags") { parseEnums(Def, true, PE->getSubExpr(), Sema); } else if (key == "qt_qobject") { Def.HasQObject = true; } else if (key == "qt_fake") { Def.HasQGadget = false; } else if (key == "qt_qgadget") { Def.HasQGadget = true; } else if (key == "qt_classinfo") { clang::StringLiteral *Val1 = nullptr, *Val2 = nullptr; std::tie(Val1, Val2) = ExtractLiterals(PE, PP, "Q_CLASSINFO", "Expected string literal in Q_CLASSINFO"); if (Val1 && Val2) { Def.ClassInfo.emplace_back(Val1->getString(), Val2->getString()); } } else if (key == "qt_interfaces") { parseInterfaces(Def, PE->getSubExpr(), Sema); } else if (key == "qt_plugin_metadata") { parsePluginMetaData(Def, PE->getSubExpr(), Sema); HasPlugin = true; } } } else if (clang::CXXMethodDecl *M = llvm::dyn_cast<clang::CXXMethodDecl>(*it)) { for (auto attr_it = M->specific_attr_begin<clang::AnnotateAttr>(); attr_it != M->specific_attr_end<clang::AnnotateAttr>(); ++attr_it) { const clang::AnnotateAttr *A = *attr_it; if (A->getAnnotation() == "qt_signal") { Def.Signals.push_back(M); } else if (A->getAnnotation() == "qt_slot") { Def.Slots.push_back(M); } else if (A->getAnnotation() == "qt_invokable" || A->getAnnotation() == "qt_scriptable" ) { if (auto *C = llvm::dyn_cast<clang::CXXConstructorDecl>(M)) { Def.Constructors.push_back(C); } else { Def.Methods.push_back(M); } } else if (A->getAnnotation().startswith("qt_revision:")) { Def.RevisionMethodCount++; } } } } //Check notify Signals for (PropertyDef &P: Def.Properties) { if (!P.notify.Str.empty()) { int Idx = 0; for (clang::CXXMethodDecl *MD : Def.Signals) { if (MD->getName() == P.notify.Str) { P.notify.notifyId = Idx; P.notify.MD = MD; break; } Idx += 1 + MD->getNumParams() - MD->getMinRequiredArguments(); } if (P.notify.notifyId < 0 ) { PP.getDiagnostics().Report(P.notify.Loc, PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "NOTIFY signal '%0' of property '%1' does not exist in class %2")) << P.notify.Str << P.name << Def.Record; } else { Def.NotifyCount++; } } if (P.revision > 0) Def.RevisionPropertyCount++; } return Def; }
static void parseEnums(ClassDef &Def, bool isFlag, clang::Expr *Content, clang::Sema &Sema) { clang::Preprocessor &PP = Sema.getPreprocessor(); clang::StringLiteral *Val = llvm::dyn_cast<clang::StringLiteral>(Content); if (!Val) { PP.getDiagnostics().Report(Content->getExprLoc(), PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "Invalid Q_ENUMS annotation")); return; } llvm::MemoryBuffer* Buf = maybe_unique(llvm::MemoryBuffer::getMemBufferCopy(Val->getString(), "Q_ENUMS")); clang::Lexer Lex(CreateFileIDForMemBuffer(PP, Buf, Content->getExprLoc()), Buf, PP.getSourceManager(), PP.getLangOpts()); clang::CXXScopeSpec SS; clang::Token Tok, Next; Lex.LexFromRawLexer(Tok); for (; !Tok.is(clang::tok::eof); Tok = Next) { Lex.LexFromRawLexer(Next); clang::IdentifierInfo* II = nullptr; if (Tok.is(clang::tok::raw_identifier)) II = PP.LookUpIdentifierInfo(Tok); if (Tok.is(clang::tok::identifier)) { if (Next.is(clang::tok::coloncolon)) { if (Sema.ActOnCXXNestedNameSpecifier(Sema.getScopeForContext(Def.Record), *II, GetFromLiteral(Tok, Val, PP), GetFromLiteral(Next, Val, PP), {}, false, SS)) SS.SetInvalid({GetFromLiteral(Tok, Val, PP), GetFromLiteral(Next, Val, PP)}); Lex.LexFromRawLexer(Next); continue; } clang::LookupResult Found(Sema, II, GetFromLiteral(Tok, Val, PP), clang::Sema::LookupNestedNameSpecifierName); if (SS.isEmpty()) Sema.LookupQualifiedName(Found, Def.Record); else { clang::DeclContext* DC = Sema.computeDeclContext(SS); Sema.LookupQualifiedName(Found, DC ? DC : Def.Record); } llvm::StringRef Alias; clang::EnumDecl* R = Found.getAsSingle<clang::EnumDecl>(); if (!R) { if (clang::TypedefDecl *TD = Found.getAsSingle<clang::TypedefDecl>()) { const clang::EnumType* ET = TD->getUnderlyingType()->getAs<clang::EnumType>(); const clang::TemplateSpecializationType* TDR = TD->getUnderlyingType()->getAs<clang::TemplateSpecializationType>(); if(TDR && TDR->getNumArgs() == 1 && TDR->getTemplateName().getAsTemplateDecl()->getName() == "QFlags") ET = TDR->getArg(0).getAsType()->getAs<clang::EnumType>(); if (ET) { R = ET->getDecl(); if (TD->getIdentifier()) Alias = TD->getName(); } } } if (Found.empty() || !R) { // TODO: typo correction // This should be an error, but the official moc do not understand that as an error. PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Warning, "no enum names %0")) << Found.getLookupName(); break; } if (R->getDeclContext() == Def.Record) { if (Alias.empty() && R->getIdentifier()) Alias = R->getName(); Def.addEnum(R, Alias.empty() ? R->getNameAsString() : std::string(Alias), isFlag); } else if (R->getDeclContext()->isRecord() && llvm::isa<clang::CXXRecordDecl>(R->getDeclContext())) { // TODO: check it is a QObject Def.addExtra(llvm::cast<clang::CXXRecordDecl>(R->getDeclContext())); } SS.clear(); continue; } else if (Tok.is(clang::tok::coloncolon)) { if (SS.isEmpty()) { SS.MakeGlobal(Sema.getASTContext(), GetFromLiteral(Tok, Val, PP)); continue; } } PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "Invalid token in Q_ENUMS")); break; } }
static void parsePluginMetaData(ClassDef &Def, clang::Expr *Content, clang::Sema &Sema) { clang::Preprocessor &PP = Sema.getPreprocessor(); clang::StringLiteral *Val = llvm::dyn_cast<clang::StringLiteral>(Content); if (!Val) { PP.getDiagnostics().Report(Content->getExprLoc(), PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "Invalid Q_PLUGIN_METADATA annotation")); return; } llvm::MemoryBuffer* Buf = maybe_unique(llvm::MemoryBuffer::getMemBufferCopy(Val->getString(), "Q_PLUGIN_METADATA")); clang::Lexer Lex(CreateFileIDForMemBuffer(PP, Buf, Content->getExprLoc()), Buf, PP.getSourceManager(), PP.getLangOpts()); clang::Token Tok; Lex.LexFromRawLexer(Tok); while (Tok.is(clang::tok::raw_identifier)) { clang::IdentifierInfo *II = PP.LookUpIdentifierInfo(Tok); if (II->getName() != "IID" && II->getName() != "FILE") { Lex.LexFromRawLexer(Tok); continue; } Lex.LexFromRawLexer(Tok); if (!Tok.is(clang::tok::string_literal)) { PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "Expected string literal")); return; } llvm::SmallVector<clang::Token, 4> StrToks; do { StrToks.push_back(Tok); Lex.LexFromRawLexer(Tok); } while (Tok.is(clang::tok::string_literal)); #if CLANG_VERSION_MAJOR!=3 || CLANG_VERSION_MINOR>4 clang::StringLiteralParser Literal(StrToks, PP); #else clang::StringLiteralParser Literal(&StrToks[0], StrToks.size(), PP); #endif if (Literal.hadError) return; if (II->getName() == "IID") Def.Plugin.IID = Literal.GetString(); else { llvm::StringRef Filename = Literal.GetString(); const clang::DirectoryLookup *CurDir; const clang::FileEntry *File = PP.LookupFile( #if CLANG_VERSION_MAJOR!=3 || CLANG_VERSION_MINOR>3 Val->getLocStart(), #endif Filename, false, nullptr, #if CLANG_VERSION_MAJOR!=3 || CLANG_VERSION_MINOR>5 nullptr, #endif CurDir, nullptr, nullptr, nullptr); if (!File) { PP.getDiagnostics().Report(GetFromLiteral(StrToks.front(), Val, PP), clang::diag::err_pp_file_not_found) << Filename; return; } const llvm::MemoryBuffer* JSonBuf = PP.getSourceManager().getMemoryBufferForFile(File); llvm::SourceMgr SM; llvm::yaml::Stream YAMLStream(JSonBuf->getBuffer(), SM); llvm::yaml::document_iterator I = YAMLStream.begin(); if (I == YAMLStream.end() || !I->getRoot() || !QBJS::Parse(I->getRoot(), Def.Plugin.MetaData)) { // FIXME PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "Error pwhile parsing JSON")); return; } } } if (!Tok.is(clang::tok::eof)) { PP.getDiagnostics().Report(GetFromLiteral(Tok, Val, PP), PP.getDiagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, "Parse error: Expected 'IID' or 'FILE'")); return; } }