static SourceLoc getDeclStartPosition(SourceFile &File) { SourceManager &SM = File.getASTContext().SourceMgr; SourceLoc Winner; auto tryUpdateStart = [&](SourceLoc Loc) -> bool { if (Loc.isInvalid()) return false; if (Winner.isInvalid()) { Winner = Loc; return true; } if (SM.isBeforeInBuffer(Loc, Winner)) { Winner = Loc; return true; } return false; }; for (auto D : File.Decls) { if (tryUpdateStart(D->getStartLoc())) { tryUpdateStart(D->getAttrs().getStartLoc()); auto RawComment = D->getRawComment(); if (!RawComment.isEmpty()) tryUpdateStart(RawComment.Comments.front().Range.getStart()); } } return Winner; }
bool SemaAnnotator::passCallArgNames(Expr *Fn, TupleExpr *TupleE) { ValueDecl *D = extractDecl(Fn); if (!D) return true; // continue. ArrayRef<Identifier> ArgNames = TupleE->getElementNames(); ArrayRef<SourceLoc> ArgLocs = TupleE->getElementNameLocs(); for (auto i : indices(ArgNames)) { Identifier Name = ArgNames[i]; if (Name.empty()) continue; SourceLoc Loc = ArgLocs[i]; if (Loc.isInvalid()) continue; CharSourceRange Range{ Loc, Name.getLength() }; bool Continue = SEWalker.visitCallArgName(Name, Range, D); if (!Continue) { Cancelled = true; return false; } } return true; }
static unsigned getLineNumber(DCType *DC) { SourceLoc loc = DC->getLoc(); if (loc.isInvalid()) return 0; const ASTContext &ctx = static_cast<const DeclContext *>(DC)->getASTContext(); return ctx.SourceMgr.getLineAndColumn(loc).first; }
void SerializedDiagnosticConsumer:: emitDiagnosticMessage(SourceManager &SM, SourceLoc Loc, DiagnosticKind Kind, StringRef Text, const DiagnosticInfo &Info) { // Emit the diagnostic to bitcode. llvm::BitstreamWriter &Stream = State->Stream; RecordData &Record = State->Record; AbbreviationMap &Abbrevs = State->Abbrevs; StringRef filename = ""; if (Loc.isValid()) filename = SM.getIdentifierForBuffer(SM.findBufferContainingLoc(Loc)); // Emit the RECORD_DIAG record. Record.clear(); Record.push_back(RECORD_DIAG); Record.push_back(getDiagnosticLevel(Kind)); addLocToRecord(Loc, SM, filename, Record); // FIXME: Swift diagnostics currently have no category. Record.push_back(0); // FIXME: Swift diagnostics currently have no flags. Record.push_back(0); // Emit the message. Record.push_back(Text.size()); Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, Text); // If the location is invalid, do not emit source ranges or fixits. if (Loc.isInvalid()) return; // Emit source ranges. auto RangeAbbrev = State->Abbrevs.get(RECORD_SOURCE_RANGE); for (const auto &R : Info.Ranges) { if (R.isInvalid()) continue; State->Record.clear(); State->Record.push_back(RECORD_SOURCE_RANGE); addRangeToRecord(R, SM, filename, State->Record); State->Stream.EmitRecordWithAbbrev(RangeAbbrev, State->Record); } // Emit FixIts. auto FixItAbbrev = State->Abbrevs.get(RECORD_FIXIT); for (const auto &F : Info.FixIts) { if (F.getRange().isValid()) { State->Record.clear(); State->Record.push_back(RECORD_FIXIT); addRangeToRecord(F.getRange(), SM, filename, State->Record); State->Record.push_back(F.getText().size()); Stream.EmitRecordWithBlob(FixItAbbrev, Record, F.getText()); } } }
void swift::lookupVisibleDecls(VisibleDeclConsumer &Consumer, const DeclContext *DC, LazyResolver *TypeResolver, bool IncludeTopLevel, SourceLoc Loc) { if (Loc.isInvalid()) { lookupVisibleDeclsImpl(Consumer, DC, TypeResolver, IncludeTopLevel, Loc); return; } // Filtering out unusable values. class LocalConsumer : public VisibleDeclConsumer { const SourceManager &SM; SourceLoc Loc; VisibleDeclConsumer &Consumer; bool isUsableValue(ValueDecl *VD, DeclVisibilityKind Reason) { // Check "use within its own initial value" case. if (auto *varD = dyn_cast<VarDecl>(VD)) if (auto *PBD = varD->getParentPatternBinding()) if (!PBD->isImplicit() && SM.rangeContainsTokenLoc(PBD->getSourceRange(), Loc)) return false; switch (Reason) { case DeclVisibilityKind::LocalVariable: // Use of 'TypeDecl's before declaration is allowed. if (isa<TypeDecl>(VD)) return true; return SM.isBeforeInBuffer(VD->getLoc(), Loc); case DeclVisibilityKind::VisibleAtTopLevel: // TODO: Implement forward reference rule for script mode? Currently, // it's not needed because the rest of the file hasn't been parsed. // See: https://bugs.swift.org/browse/SR-284 for the rule. return true; default: // Other visibility kind are always usable. return true; } } public: LocalConsumer(const SourceManager &SM, SourceLoc Loc, VisibleDeclConsumer &Consumer) : SM(SM), Loc(Loc), Consumer(Consumer) {} void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason) { if (isUsableValue(VD, Reason)) Consumer.foundDecl(VD, Reason); } } LocalConsumer(DC->getASTContext().SourceMgr, Loc, Consumer); lookupVisibleDeclsImpl(LocalConsumer, DC, TypeResolver, IncludeTopLevel, Loc); }
void handlePrimaryAST(ASTUnitRef AstUnit) override { auto &CompInst = AstUnit->getCompilerInstance(); auto &SrcFile = AstUnit->getPrimarySourceFile(); trace::TracedOperation TracedOp; SmallVector<std::pair<unsigned, unsigned>, 8> Ranges; auto Action = [&]() { if (trace::enabled()) { trace::SwiftInvocation SwiftArgs; Invok->raw(SwiftArgs.Args.Args, SwiftArgs.Args.PrimaryFile); trace::initTraceFiles(SwiftArgs, CompInst); TracedOp.start(trace::OperationKind::RelatedIdents, SwiftArgs, {std::make_pair("Offset", std::to_string(Offset))}); } unsigned BufferID = SrcFile.getBufferID().getValue(); SourceLoc Loc = Lexer::getLocForStartOfToken(CompInst.getSourceMgr(), BufferID, Offset); if (Loc.isInvalid()) return; SemaLocResolver Resolver(SrcFile); SemaToken SemaTok = Resolver.resolve(Loc); if (SemaTok.isInvalid()) return; if (SemaTok.IsKeywordArgument) return; ValueDecl *VD = SemaTok.CtorTyRef ? SemaTok.CtorTyRef : SemaTok.ValueD; if (!VD) return; // This was a module reference. // Only accept pointing to an identifier. if (!SemaTok.IsRef && (isa<ConstructorDecl>(VD) || isa<DestructorDecl>(VD) || isa<SubscriptDecl>(VD))) return; if (VD->getName().isOperator()) return; RelatedIdScanner Scanner(SrcFile, BufferID, VD, Ranges); if (DeclContext *LocalDC = VD->getDeclContext()->getLocalContext()) { Scanner.walk(LocalDC); } else { Scanner.walk(SrcFile); } }; Action(); RelatedIdentsInfo Info; Info.Ranges = Ranges; Receiver(Info); }
Optional<FileSpecificDiagnosticConsumer::Subconsumer *> FileSpecificDiagnosticConsumer::subconsumerForLocation(SourceManager &SM, SourceLoc loc) { // Diagnostics with invalid locations always go to every consumer. if (loc.isInvalid()) return None; // What if a there's a FileSpecificDiagnosticConsumer but there are no // subconsumers in it? (This situation occurs for the fix-its // FileSpecificDiagnosticConsumer.) In such a case, bail out now. if (Subconsumers.empty()) return None; // This map is generated on first use and cached, to allow the // FileSpecificDiagnosticConsumer to be set up before the source files are // actually loaded. if (ConsumersOrderedByRange.empty()) { // It's possible to get here while a bridging header PCH is being // attached-to, if there's some sort of AST-reader warning or error, which // happens before CompilerInstance::setUpInputs(), at which point _no_ // source buffers are loaded in yet. In that case we return None, rather // than trying to build a nonsensical map (and actually crashing since we // can't find buffers for the inputs). assert(!Subconsumers.empty()); if (!SM.getIDForBufferIdentifier(Subconsumers.begin()->getInputFileName()) .hasValue()) { assert(llvm::none_of(Subconsumers, [&](const Subconsumer &subconsumer) { return SM.getIDForBufferIdentifier(subconsumer.getInputFileName()) .hasValue(); })); return None; } auto *mutableThis = const_cast<FileSpecificDiagnosticConsumer*>(this); mutableThis->computeConsumersOrderedByRange(SM); } // This std::lower_bound call is doing a binary search for the first range // that /might/ contain 'loc'. Specifically, since the ranges are sorted // by end location, it's looking for the first range where the end location // is greater than or equal to 'loc'. const ConsumerAndRange *possiblyContainingRangeIter = std::lower_bound( ConsumersOrderedByRange.begin(), ConsumersOrderedByRange.end(), loc, [](const ConsumerAndRange &entry, SourceLoc loc) -> bool { return entry.endsAfter(loc); }); if (possiblyContainingRangeIter != ConsumersOrderedByRange.end() && possiblyContainingRangeIter->contains(loc)) { auto *consumerAndRangeForLocation = const_cast<ConsumerAndRange *>(possiblyContainingRangeIter); return &(*this)[*consumerAndRangeForLocation]; } return None; }
void handlePrimaryAST(ASTUnitRef AstUnit) override { auto &CompIns = AstUnit->getCompilerInstance(); Module *MainModule = CompIns.getMainModule(); unsigned BufferID = AstUnit->getPrimarySourceFile().getBufferID().getValue(); SourceLoc Loc = Lexer::getLocForStartOfToken(CompIns.getSourceMgr(), BufferID, Offset); if (Loc.isInvalid()) { Receiver({}); return; } trace::TracedOperation TracedOp; if (trace::enabled()) { trace::SwiftInvocation SwiftArgs; ASTInvok->raw(SwiftArgs.Args.Args, SwiftArgs.Args.PrimaryFile); trace::initTraceFiles(SwiftArgs, CompIns); TracedOp.start(trace::OperationKind::CursorInfoForSource, SwiftArgs, {std::make_pair("Offset", std::to_string(Offset))}); } SemaLocResolver Resolver(AstUnit->getPrimarySourceFile()); SemaToken SemaTok = Resolver.resolve(Loc); if (SemaTok.isInvalid()) { Receiver({}); return; } CompilerInvocation CompInvok; ASTInvok->applyTo(CompInvok); if (SemaTok.Mod) { passCursorInfoForModule(SemaTok.Mod, Lang.getIFaceGenContexts(), CompInvok, Receiver); } else { ValueDecl *VD = SemaTok.CtorTyRef ? SemaTok.CtorTyRef : SemaTok.ValueD; bool Failed = passCursorInfoForDecl(VD, MainModule, SemaTok.Ty, SemaTok.IsRef, BufferID, Lang, CompInvok, PreviousASTSnaps, Receiver); if (Failed) { if (!PreviousASTSnaps.empty()) { // Attempt again using the up-to-date AST. resolveCursor(Lang, InputFile, Offset, ASTInvok, /*TryExistingAST=*/false, Receiver); } else { Receiver({}); } } } }
Optional<FileSpecificDiagnosticConsumer::ConsumerSpecificInformation *> FileSpecificDiagnosticConsumer::consumerSpecificInformationForLocation( SourceManager &SM, SourceLoc loc) const { // Diagnostics with invalid locations always go to every consumer. if (loc.isInvalid()) return None; // This map is generated on first use and cached, to allow the // FileSpecificDiagnosticConsumer to be set up before the source files are // actually loaded. if (ConsumersOrderedByRange.empty()) { // It's possible to get here while a bridging header PCH is being // attached-to, if there's some sort of AST-reader warning or error, which // happens before CompilerInstance::setUpInputs(), at which point _no_ // source buffers are loaded in yet. In that case we return None, rather // than trying to build a nonsensical map (and actually crashing since we // can't find buffers for the inputs). assert(!SubConsumers.empty()); if (!SM.getIDForBufferIdentifier(SubConsumers.begin()->first).hasValue()) { assert(llvm::none_of(SubConsumers, [&](const ConsumerPair &pair) { return SM.getIDForBufferIdentifier(pair.first).hasValue(); })); return None; } auto *mutableThis = const_cast<FileSpecificDiagnosticConsumer*>(this); mutableThis->computeConsumersOrderedByRange(SM); } // This std::lower_bound call is doing a binary search for the first range // that /might/ contain 'loc'. Specifically, since the ranges are sorted // by end location, it's looking for the first range where the end location // is greater than or equal to 'loc'. const ConsumerSpecificInformation *possiblyContainingRangeIter = std::lower_bound( ConsumersOrderedByRange.begin(), ConsumersOrderedByRange.end(), loc, [](const ConsumerSpecificInformation &entry, SourceLoc loc) -> bool { auto compare = std::less<const char *>(); return compare(getRawLoc(entry.range.getEnd()).getPointer(), getRawLoc(loc).getPointer()); }); if (possiblyContainingRangeIter != ConsumersOrderedByRange.end() && possiblyContainingRangeIter->range.contains(loc)) { return const_cast<ConsumerSpecificInformation *>( possiblyContainingRangeIter); } return None; }
Optional<FileSpecificDiagnosticConsumer::Subconsumer *> FileSpecificDiagnosticConsumer::findSubconsumerForNonNote( SourceManager &SM, const SourceLoc loc, const SourceLoc bufferIndirectlyCausingDiagnostic) { const auto subconsumer = subconsumerForLocation(SM, loc); if (!subconsumer) return None; // No place to put it; might be in an imported module if ((*subconsumer)->getConsumer()) return subconsumer; // A primary file with a .dia file // Try to put it in the responsible primary input if (bufferIndirectlyCausingDiagnostic.isInvalid()) return None; const auto currentPrimarySubconsumer = subconsumerForLocation(SM, bufferIndirectlyCausingDiagnostic); assert(!currentPrimarySubconsumer || (*currentPrimarySubconsumer)->getConsumer() && "current primary must have a .dia file"); return currentPrimarySubconsumer; }
bool IndexSwiftASTWalker::startEntityRef(ValueDecl *D, SourceLoc Loc) { if (!shouldIndex(D)) return false; if (Loc.isInvalid()) return false; if (isa<AbstractFunctionDecl>(D)) { CallRefEntityInfo Info; if (initCallRefEntityInfo(ExprStack.back(), getParentExpr(), D, Loc, Info)) return false; return startEntity(D, Info); } else { EntityInfo Info; if (initEntityInfo(D, Loc, /*isRef=*/true, Info)) return false; return startEntity(D, Info); } }
bool IndexSwiftASTWalker::startEntityDecl(ValueDecl *D) { if (!shouldIndex(D)) return false; SourceLoc Loc = D->getLoc(); if (Loc.isInvalid() && !IsModuleFile) return false; if (isa<FuncDecl>(D)) { FuncDeclEntityInfo Info; if (initFuncDeclEntityInfo(D, Info)) return false; return startEntity(D, Info); } else { EntityInfo Info; if (initEntityInfo(D, Loc, /*isRef=*/false, Info)) return false; return startEntity(D, Info); } }
void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) { auto behavior = state.determineBehavior(diagnostic.getID()); if (behavior == DiagnosticState::Behavior::Ignore) return; // Figure out the source location. SourceLoc loc = diagnostic.getLoc(); if (loc.isInvalid() && diagnostic.getDecl()) { const Decl *decl = diagnostic.getDecl(); // If a declaration was provided instead of a location, and that declaration // has a location we can point to, use that location. loc = decl->getLoc(); if (loc.isInvalid()) { // There is no location we can point to. Pretty-print the declaration // so we can point to it. SourceLoc ppLoc = PrettyPrintedDeclarations[decl]; if (ppLoc.isInvalid()) { class TrackingPrinter : public StreamPrinter { SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries; public: TrackingPrinter( SmallVectorImpl<std::pair<const Decl *, uint64_t>> &Entries, raw_ostream &OS) : StreamPrinter(OS), Entries(Entries) {} void printDeclLoc(const Decl *D) override { Entries.push_back({ D, OS.tell() }); } }; SmallVector<std::pair<const Decl *, uint64_t>, 8> entries; llvm::SmallString<128> buffer; llvm::SmallString<128> bufferName; { // Figure out which declaration to print. It's the top-most // declaration (not a module). const Decl *ppDecl = decl; auto dc = decl->getDeclContext(); // FIXME: Horrible, horrible hackaround. We're not getting a // DeclContext everywhere we should. if (!dc) { return; } while (!dc->isModuleContext()) { switch (dc->getContextKind()) { case DeclContextKind::Module: llvm_unreachable("Not in a module context!"); break; case DeclContextKind::FileUnit: case DeclContextKind::TopLevelCodeDecl: break; case DeclContextKind::ExtensionDecl: ppDecl = cast<ExtensionDecl>(dc); break; case DeclContextKind::GenericTypeDecl: ppDecl = cast<GenericTypeDecl>(dc); break; case DeclContextKind::SerializedLocal: case DeclContextKind::Initializer: case DeclContextKind::AbstractClosureExpr: case DeclContextKind::AbstractFunctionDecl: case DeclContextKind::SubscriptDecl: break; } dc = dc->getParent(); } // Build the module name path (in reverse), which we use to // build the name of the buffer. SmallVector<StringRef, 4> nameComponents; while (dc) { nameComponents.push_back(cast<Module>(dc)->getName().str()); dc = dc->getParent(); } for (unsigned i = nameComponents.size(); i; --i) { bufferName += nameComponents[i-1]; bufferName += '.'; } if (auto value = dyn_cast<ValueDecl>(ppDecl)) { bufferName += value->getNameStr(); } else if (auto ext = dyn_cast<ExtensionDecl>(ppDecl)) { bufferName += ext->getExtendedType().getString(); } // Pretty-print the declaration we've picked. llvm::raw_svector_ostream out(buffer); TrackingPrinter printer(entries, out); ppDecl->print(printer, PrintOptions::printForDiagnostics()); } // Build a buffer with the pretty-printed declaration. auto bufferID = SourceMgr.addMemBufferCopy(buffer, bufferName); auto memBufferStartLoc = SourceMgr.getLocForBufferStart(bufferID); // Go through all of the pretty-printed entries and record their // locations. for (auto entry : entries) { PrettyPrintedDeclarations[entry.first] = memBufferStartLoc.getAdvancedLoc(entry.second); } // Grab the pretty-printed location. ppLoc = PrettyPrintedDeclarations[decl]; } loc = ppLoc; } } // Actually substitute the diagnostic arguments into the diagnostic text. llvm::SmallString<256> Text; { llvm::raw_svector_ostream Out(Text); formatDiagnosticText(diagnosticStrings[(unsigned)diagnostic.getID()], diagnostic.getArgs(), Out); } // Pass the diagnostic off to the consumer. DiagnosticInfo Info; Info.ID = diagnostic.getID(); Info.Ranges = diagnostic.getRanges(); Info.FixIts = diagnostic.getFixIts(); for (auto &Consumer : Consumers) { Consumer->handleDiagnostic(SourceMgr, loc, toDiagnosticKind(behavior), Text, Info); } }
void AssignmentFailure::fixItChangeInoutArgType(const Expr *arg, Type actualType, Type neededType) const { auto *DC = getDC(); auto *DRE = dyn_cast<DeclRefExpr>(arg); if (!DRE) return; auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl()); if (!VD) return; // Don't emit for non-local variables. // (But in script-mode files, we consider module-scoped // variables in the same file to be local variables.) auto VDC = VD->getDeclContext(); bool isLocalVar = VDC->isLocalContext(); if (!isLocalVar && VDC->isModuleScopeContext()) { auto argFile = DC->getParentSourceFile(); auto varFile = VDC->getParentSourceFile(); isLocalVar = (argFile == varFile && argFile->isScriptMode()); } if (!isLocalVar) return; SmallString<32> scratch; SourceLoc endLoc; // Filled in if we decide to diagnose this SourceLoc startLoc; // Left invalid if we're inserting auto isSimpleTypelessPattern = [](Pattern *P) -> bool { if (auto VP = dyn_cast_or_null<VarPattern>(P)) P = VP->getSubPattern(); return P && isa<NamedPattern>(P); }; auto typeRange = VD->getTypeSourceRangeForDiagnostics(); if (typeRange.isValid()) { startLoc = typeRange.Start; endLoc = typeRange.End; } else if (isSimpleTypelessPattern(VD->getParentPattern())) { endLoc = VD->getNameLoc(); scratch += ": "; } if (endLoc.isInvalid()) return; scratch += neededType.getString(); // Adjust into the location where we actually want to insert endLoc = Lexer::getLocForEndOfToken(getASTContext().SourceMgr, endLoc); // Since we already adjusted endLoc, this will turn an insertion // into a zero-character replacement. if (!startLoc.isValid()) startLoc = endLoc; emitDiagnostic(VD->getLoc(), diag::inout_change_var_type_if_possible, actualType, neededType) .fixItReplaceChars(startLoc, endLoc, scratch); }
ParserStatus Parser::parseParameterClause(SourceLoc &leftParenLoc, SmallVectorImpl<ParsedParameter> ¶ms, SourceLoc &rightParenLoc, DefaultArgumentInfo *defaultArgs, ParameterContextKind paramContext) { assert(params.empty() && leftParenLoc.isInvalid() && rightParenLoc.isInvalid() && "Must start with empty state"); // Consume the starting '('; leftParenLoc = consumeToken(tok::l_paren); // Trivial case: empty parameter list. if (Tok.is(tok::r_paren)) { rightParenLoc = consumeToken(tok::r_paren); return ParserStatus(); } // Parse the parameter list. bool isClosure = paramContext == ParameterContextKind::Closure; return parseList(tok::r_paren, leftParenLoc, rightParenLoc, tok::comma, /*OptionalSep=*/false, /*AllowSepAfterLast=*/false, diag::expected_rparen_parameter, [&]() -> ParserStatus { ParsedParameter param; ParserStatus status; SourceLoc StartLoc = Tok.getLoc(); unsigned defaultArgIndex = defaultArgs ? defaultArgs->NextIndex++ : 0; // Attributes. bool FoundCCToken; parseDeclAttributeList(param.Attrs, FoundCCToken, /*stop at type attributes*/true, true); if (FoundCCToken) { if (CodeCompletion) { CodeCompletion->completeDeclAttrKeyword(nullptr, isInSILMode(), true); } else { status |= makeParserCodeCompletionStatus(); } } // ('inout' | 'let' | 'var')? if (Tok.is(tok::kw_inout)) { param.LetVarInOutLoc = consumeToken(); param.SpecifierKind = ParsedParameter::InOut; } else if (Tok.is(tok::kw_let)) { param.LetVarInOutLoc = consumeToken(); param.SpecifierKind = ParsedParameter::Let; } else if (Tok.is(tok::kw_var)) { diagnose(Tok.getLoc(), diag::var_parameter_not_allowed) .fixItRemove(Tok.getLoc()); param.LetVarInOutLoc = consumeToken(); param.SpecifierKind = ParsedParameter::Var; } // Redundant specifiers are fairly common, recognize, reject, and recover // from this gracefully. if (Tok.isAny(tok::kw_inout, tok::kw_let, tok::kw_var)) { diagnose(Tok, diag::parameter_inout_var_let) .fixItRemove(Tok.getLoc()); consumeToken(); param.isInvalid = true; } if (startsParameterName(*this, isClosure)) { // identifier-or-none for the first name if (Tok.is(tok::kw__)) { param.FirstNameLoc = consumeToken(); } else { assert(Tok.canBeArgumentLabel() && "startsParameterName() lied"); param.FirstName = Context.getIdentifier(Tok.getText()); param.FirstNameLoc = consumeToken(); } // identifier-or-none? for the second name if (Tok.canBeArgumentLabel()) { if (!Tok.is(tok::kw__)) param.SecondName = Context.getIdentifier(Tok.getText()); param.SecondNameLoc = consumeToken(); } // Operators and closures cannot have API names. if ((paramContext == ParameterContextKind::Operator || paramContext == ParameterContextKind::Closure) && !param.FirstName.empty() && param.SecondNameLoc.isValid()) { diagnose(param.FirstNameLoc, diag::parameter_operator_keyword_argument, isClosure) .fixItRemoveChars(param.FirstNameLoc, param.SecondNameLoc); param.FirstName = param.SecondName; param.FirstNameLoc = param.SecondNameLoc; param.SecondName = Identifier(); param.SecondNameLoc = SourceLoc(); } // (':' type)? if (consumeIf(tok::colon)) { auto type = parseType(diag::expected_parameter_type); status |= type; param.Type = type.getPtrOrNull(); // If we didn't parse a type, then we already diagnosed that the type // was invalid. Remember that. if (type.isParseError() && !type.hasCodeCompletion()) param.isInvalid = true; // Only allow 'inout' before the parameter name. if (auto InOutTy = dyn_cast_or_null<InOutTypeRepr>(param.Type)) { SourceLoc InOutLoc = InOutTy->getInOutLoc(); SourceLoc NameLoc = param.FirstNameLoc; diagnose(InOutLoc, diag::inout_must_appear_before_param) .fixItRemove(InOutLoc) .fixItInsert(NameLoc, "inout "); param.Type = InOutTy->getBase(); } } } else { // Otherwise, we have invalid code. Check to see if this looks like a // type. If so, diagnose it as a common error. bool isBareType = false; { BacktrackingScope backtrack(*this); isBareType = canParseType() && Tok.isAny(tok::comma, tok::r_paren, tok::equal); } if (isBareType) { // Otherwise, if this is a bare type, then the user forgot to name the // parameter, e.g. "func foo(Int) {}" SourceLoc typeStartLoc = Tok.getLoc(); auto type = parseType(diag::expected_parameter_type, false); status |= type; param.Type = type.getPtrOrNull(); // Unnamed parameters must be written as "_: Type". if (param.Type) { diagnose(typeStartLoc, diag::parameter_unnamed) .fixItInsert(typeStartLoc, "_: "); } else { param.isInvalid = true; } } else { // Otherwise, we're not sure what is going on, but this doesn't smell // like a parameter. diagnose(Tok, diag::expected_parameter_name); param.isInvalid = true; } } // '...'? if (Tok.isEllipsis()) param.EllipsisLoc = consumeToken(); // ('=' expr)? if (Tok.is(tok::equal)) { SourceLoc EqualLoc = Tok.getLoc(); status |= parseDefaultArgument(*this, defaultArgs, defaultArgIndex, param.DefaultArg, paramContext); if (param.EllipsisLoc.isValid() && param.DefaultArg) { // The range of the complete default argument. SourceRange defaultArgRange; if (auto init = param.DefaultArg->getExpr()) defaultArgRange = SourceRange(param.EllipsisLoc, init->getEndLoc()); diagnose(EqualLoc, diag::parameter_vararg_default) .highlight(param.EllipsisLoc) .fixItRemove(defaultArgRange); param.isInvalid = true; param.DefaultArg = nullptr; } } // If we haven't made progress, don't add the parameter. if (Tok.getLoc() == StartLoc) { // If we took a default argument index for this parameter, but didn't add // one, then give it back. if (defaultArgs) defaultArgs->NextIndex--; return status; } params.push_back(param); return status; }); }
/// parseTypeTupleBody /// type-tuple: /// '(' type-tuple-body? ')' /// type-tuple-body: /// type-tuple-element (',' type-tuple-element)* '...'? /// type-tuple-element: /// identifier ':' type /// type ParserResult<TupleTypeRepr> Parser::parseTypeTupleBody() { Parser::StructureMarkerRAII ParsingTypeTuple(*this, Tok); SourceLoc RPLoc, LPLoc = consumeToken(tok::l_paren); SourceLoc EllipsisLoc; unsigned EllipsisIdx; SmallVector<TypeRepr *, 8> ElementsR; ParserStatus Status = parseList(tok::r_paren, LPLoc, RPLoc, tok::comma, /*OptionalSep=*/false, /*AllowSepAfterLast=*/false, diag::expected_rparen_tuple_type_list, [&] () -> ParserStatus { // If this is an inout marker in an argument list, consume the inout. SourceLoc InOutLoc; consumeIf(tok::kw_inout, InOutLoc); // If the tuple element starts with "ident :", then // the identifier is an element tag, and it is followed by a type // annotation. if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) { // Consume the name Identifier name; if (!Tok.is(tok::kw__)) name = Context.getIdentifier(Tok.getText()); SourceLoc nameLoc = consumeToken(); // Consume the ':'. consumeToken(tok::colon); // Parse the type annotation. ParserResult<TypeRepr> type = parseType(diag::expected_type); if (type.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (type.isNull()) return makeParserError(); // If an 'inout' marker was specified, build the type. Note that we bury // the inout locator within the named locator. This is weird but required // by sema apparently. if (InOutLoc.isValid()) type = makeParserResult(new (Context) InOutTypeRepr(type.get(), InOutLoc)); ElementsR.push_back( new (Context) NamedTypeRepr(name, type.get(), nameLoc)); } else { // Otherwise, this has to be a type. ParserResult<TypeRepr> type = parseType(); if (type.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (type.isNull()) return makeParserError(); if (InOutLoc.isValid()) type = makeParserResult(new (Context) InOutTypeRepr(type.get(), InOutLoc)); ElementsR.push_back(type.get()); } // Parse '= expr' here so we can complain about it directly, rather // than dying when we see it. if (Tok.is(tok::equal)) { SourceLoc equalLoc = consumeToken(tok::equal); auto init = parseExpr(diag::expected_init_value); auto inFlight = diagnose(equalLoc, diag::tuple_type_init); if (init.isNonNull()) inFlight.fixItRemove(SourceRange(equalLoc, init.get()->getEndLoc())); } if (Tok.isEllipsis()) { if (EllipsisLoc.isValid()) { diagnose(Tok, diag::multiple_ellipsis_in_tuple) .highlight(EllipsisLoc) .fixItRemove(Tok.getLoc()); (void)consumeToken(); } else { EllipsisLoc = consumeToken(); EllipsisIdx = ElementsR.size() - 1; } } return makeParserSuccess(); }); if (EllipsisLoc.isValid() && ElementsR.empty()) { EllipsisLoc = SourceLoc(); } if (EllipsisLoc.isInvalid()) EllipsisIdx = ElementsR.size(); return makeParserResult(Status, TupleTypeRepr::create(Context, ElementsR, SourceRange(LPLoc, RPLoc), EllipsisLoc, EllipsisIdx)); }
/// parseTypeTupleBody /// type-tuple: /// '(' type-tuple-body? ')' /// type-tuple-body: /// type-tuple-element (',' type-tuple-element)* '...'? /// type-tuple-element: /// identifier ':' type /// type ParserResult<TupleTypeRepr> Parser::parseTypeTupleBody() { Parser::StructureMarkerRAII ParsingTypeTuple(*this, Tok); SourceLoc RPLoc, LPLoc = consumeToken(tok::l_paren); SourceLoc EllipsisLoc; unsigned EllipsisIdx; SmallVector<TypeRepr *, 8> ElementsR; // We keep track of the labels separately, and apply them at the end. SmallVector<std::tuple<Identifier, SourceLoc, Identifier, SourceLoc>, 4> Labels; ParserStatus Status = parseList(tok::r_paren, LPLoc, RPLoc, tok::comma, /*OptionalSep=*/false, /*AllowSepAfterLast=*/false, diag::expected_rparen_tuple_type_list, [&] () -> ParserStatus { // If this is a deprecated use of the inout marker in an argument list, // consume the inout. SourceLoc InOutLoc; bool hasAnyInOut = false; bool hasValidInOut = false; if (consumeIf(tok::kw_inout, InOutLoc)) { hasAnyInOut = true; hasValidInOut = false; } // If the tuple element starts with a potential argument label followed by a // ':' or another potential argument label, then the identifier is an // element tag, and it is followed by a type annotation. if (Tok.canBeArgumentLabel() && (peekToken().is(tok::colon) || peekToken().canBeArgumentLabel())) { // Consume the name Identifier name; if (!Tok.is(tok::kw__)) name = Context.getIdentifier(Tok.getText()); SourceLoc nameLoc = consumeToken(); // If there is a second name, consume it as well. Identifier secondName; SourceLoc secondNameLoc; if (Tok.canBeArgumentLabel()) { if (!Tok.is(tok::kw__)) secondName = Context.getIdentifier(Tok.getText()); secondNameLoc = consumeToken(); } // Consume the ':'. if (!consumeIf(tok::colon)) diagnose(Tok, diag::expected_parameter_colon); SourceLoc postColonLoc = Tok.getLoc(); // Consume 'inout' if present. if (!hasAnyInOut && consumeIf(tok::kw_inout, InOutLoc)) { hasValidInOut = true; } SourceLoc extraneousInOutLoc; while (consumeIf(tok::kw_inout, extraneousInOutLoc)) { diagnose(Tok, diag::parameter_inout_var_let_repeated) .fixItRemove(extraneousInOutLoc); } // Parse the type annotation. ParserResult<TypeRepr> type = parseType(diag::expected_type); if (type.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (type.isNull()) return makeParserError(); if (!hasValidInOut && hasAnyInOut) { diagnose(Tok.getLoc(), diag::inout_as_attr_disallowed) .fixItRemove(InOutLoc) .fixItInsert(postColonLoc, "inout "); } // If an 'inout' marker was specified, build the type. Note that we bury // the inout locator within the named locator. This is weird but required // by sema apparently. if (InOutLoc.isValid()) type = makeParserResult(new (Context) InOutTypeRepr(type.get(), InOutLoc)); // Record the label. We will look at these at the end. if (Labels.empty()) { Labels.assign(ElementsR.size(), std::make_tuple(Identifier(), SourceLoc(), Identifier(), SourceLoc())); } Labels.push_back(std::make_tuple(name, nameLoc, secondName, secondNameLoc)); ElementsR.push_back(type.get()); } else { // Otherwise, this has to be a type. ParserResult<TypeRepr> type = parseType(); if (type.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (type.isNull()) return makeParserError(); if (InOutLoc.isValid()) type = makeParserResult(new (Context) InOutTypeRepr(type.get(), InOutLoc)); if (!Labels.empty()) { Labels.push_back(std::make_tuple(Identifier(), SourceLoc(), Identifier(), SourceLoc())); } ElementsR.push_back(type.get()); } // Parse '= expr' here so we can complain about it directly, rather // than dying when we see it. if (Tok.is(tok::equal)) { SourceLoc equalLoc = consumeToken(tok::equal); auto init = parseExpr(diag::expected_init_value); auto inFlight = diagnose(equalLoc, diag::tuple_type_init); if (init.isNonNull()) inFlight.fixItRemove(SourceRange(equalLoc, init.get()->getEndLoc())); } if (Tok.isEllipsis()) { if (EllipsisLoc.isValid()) { diagnose(Tok, diag::multiple_ellipsis_in_tuple) .highlight(EllipsisLoc) .fixItRemove(Tok.getLoc()); (void)consumeToken(); } else { EllipsisLoc = consumeToken(); EllipsisIdx = ElementsR.size() - 1; } } return makeParserSuccess(); }); if (EllipsisLoc.isValid() && ElementsR.empty()) { EllipsisLoc = SourceLoc(); } if (EllipsisLoc.isInvalid()) EllipsisIdx = ElementsR.size(); // If there were any labels, figure out which labels should go into the type // representation. if (!Labels.empty()) { assert(Labels.size() == ElementsR.size()); bool isFunctionType = Tok.isAny(tok::arrow, tok::kw_throws, tok::kw_rethrows); for (unsigned i : indices(ElementsR)) { auto ¤tLabel = Labels[i]; Identifier firstName = std::get<0>(currentLabel); SourceLoc firstNameLoc = std::get<1>(currentLabel); Identifier secondName = std::get<2>(currentLabel); SourceLoc secondNameLoc = std::get<3>(currentLabel); // True tuples have labels. if (!isFunctionType) { // If there were two names, complain. if (firstNameLoc.isValid() && secondNameLoc.isValid()) { auto diag = diagnose(firstNameLoc, diag::tuple_type_multiple_labels); if (firstName.empty()) { diag.fixItRemoveChars(firstNameLoc, ElementsR[i]->getStartLoc()); } else { diag.fixItRemove( SourceRange(Lexer::getLocForEndOfToken(SourceMgr,firstNameLoc), secondNameLoc)); } } // Form the named type representation. ElementsR[i] = new (Context) NamedTypeRepr(firstName, ElementsR[i], firstNameLoc); continue; } // If there was a first name, complain; arguments in function types are // always unlabeled. if (firstNameLoc.isValid() && !firstName.empty()) { auto diag = diagnose(firstNameLoc, diag::function_type_argument_label, firstName); if (secondNameLoc.isInvalid()) diag.fixItInsert(firstNameLoc, "_ "); else if (secondName.empty()) diag.fixItRemoveChars(firstNameLoc, ElementsR[i]->getStartLoc()); else diag.fixItReplace(SourceRange(firstNameLoc), "_"); } if (firstNameLoc.isValid() || secondNameLoc.isValid()) { // Form the named parameter type representation. ElementsR[i] = new (Context) NamedTypeRepr(secondName, ElementsR[i], secondNameLoc, firstNameLoc); } } } return makeParserResult(Status, TupleTypeRepr::create(Context, ElementsR, SourceRange(LPLoc, RPLoc), EllipsisLoc, EllipsisIdx)); }
/// Performs the compile requested by the user. /// \param Instance Will be reset after performIRGeneration when the verifier /// mode is NoVerify and there were no errors. /// \returns true on error static bool performCompile(std::unique_ptr<CompilerInstance> &Instance, CompilerInvocation &Invocation, ArrayRef<const char *> Args, int &ReturnValue, FrontendObserver *observer) { FrontendOptions opts = Invocation.getFrontendOptions(); FrontendOptions::ActionType Action = opts.RequestedAction; IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); bool inputIsLLVMIr = Invocation.getInputKind() == InputFileKind::IFK_LLVM_IR; if (inputIsLLVMIr) { auto &LLVMContext = getGlobalLLVMContext(); // Load in bitcode file. assert(Invocation.getInputFilenames().size() == 1 && "We expect a single input for bitcode input!"); llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(Invocation.getInputFilenames()[0]); if (!FileBufOrErr) { Instance->getASTContext().Diags.diagnose(SourceLoc(), diag::error_open_input_file, Invocation.getInputFilenames()[0], FileBufOrErr.getError().message()); return true; } llvm::MemoryBuffer *MainFile = FileBufOrErr.get().get(); llvm::SMDiagnostic Err; std::unique_ptr<llvm::Module> Module = llvm::parseIR( MainFile->getMemBufferRef(), Err, LLVMContext); if (!Module) { // TODO: Translate from the diagnostic info to the SourceManager location // if available. Instance->getASTContext().Diags.diagnose(SourceLoc(), diag::error_parse_input_file, Invocation.getInputFilenames()[0], Err.getMessage()); return true; } // TODO: remove once the frontend understands what action it should perform IRGenOpts.OutputKind = getOutputKind(Action); return performLLVM(IRGenOpts, Instance->getASTContext(), Module.get()); } ReferencedNameTracker nameTracker; bool shouldTrackReferences = !opts.ReferenceDependenciesFilePath.empty(); if (shouldTrackReferences) Instance->setReferencedNameTracker(&nameTracker); if (Action == FrontendOptions::Parse || Action == FrontendOptions::DumpParse || Action == FrontendOptions::DumpInterfaceHash) Instance->performParseOnly(); else Instance->performSema(); if (Action == FrontendOptions::Parse) return Instance->getASTContext().hadError(); if (observer) { observer->performedSemanticAnalysis(*Instance); } FrontendOptions::DebugCrashMode CrashMode = opts.CrashMode; if (CrashMode == FrontendOptions::DebugCrashMode::AssertAfterParse) debugFailWithAssertion(); else if (CrashMode == FrontendOptions::DebugCrashMode::CrashAfterParse) debugFailWithCrash(); ASTContext &Context = Instance->getASTContext(); if (Action == FrontendOptions::REPL) { runREPL(*Instance, ProcessCmdLine(Args.begin(), Args.end()), Invocation.getParseStdlib()); return Context.hadError(); } SourceFile *PrimarySourceFile = Instance->getPrimarySourceFile(); // We've been told to dump the AST (either after parsing or type-checking, // which is already differentiated in CompilerInstance::performSema()), // so dump or print the main source file and return. if (Action == FrontendOptions::DumpParse || Action == FrontendOptions::DumpAST || Action == FrontendOptions::PrintAST || Action == FrontendOptions::DumpScopeMaps || Action == FrontendOptions::DumpTypeRefinementContexts || Action == FrontendOptions::DumpInterfaceHash) { SourceFile *SF = PrimarySourceFile; if (!SF) { SourceFileKind Kind = Invocation.getSourceFileKind(); SF = &Instance->getMainModule()->getMainSourceFile(Kind); } if (Action == FrontendOptions::PrintAST) SF->print(llvm::outs(), PrintOptions::printEverything()); else if (Action == FrontendOptions::DumpScopeMaps) { ASTScope &scope = SF->getScope(); if (opts.DumpScopeMapLocations.empty()) { scope.expandAll(); } else if (auto bufferID = SF->getBufferID()) { SourceManager &sourceMgr = Instance->getSourceMgr(); // Probe each of the locations, and dump what we find. for (auto lineColumn : opts.DumpScopeMapLocations) { SourceLoc loc = sourceMgr.getLocForLineCol(*bufferID, lineColumn.first, lineColumn.second); if (loc.isInvalid()) continue; llvm::errs() << "***Scope at " << lineColumn.first << ":" << lineColumn.second << "***\n"; auto locScope = scope.findInnermostEnclosingScope(loc); locScope->print(llvm::errs(), 0, false, false); // Dump the AST context, too. if (auto dc = locScope->getDeclContext()) { dc->printContext(llvm::errs()); } // Grab the local bindings introduced by this scope. auto localBindings = locScope->getLocalBindings(); if (!localBindings.empty()) { llvm::errs() << "Local bindings: "; interleave(localBindings.begin(), localBindings.end(), [&](ValueDecl *value) { llvm::errs() << value->getFullName(); }, [&]() { llvm::errs() << " "; }); llvm::errs() << "\n"; } } llvm::errs() << "***Complete scope map***\n"; } // Print the resulting map. scope.print(llvm::errs()); } else if (Action == FrontendOptions::DumpTypeRefinementContexts) SF->getTypeRefinementContext()->dump(llvm::errs(), Context.SourceMgr); else if (Action == FrontendOptions::DumpInterfaceHash) SF->dumpInterfaceHash(llvm::errs()); else SF->dump(); return Context.hadError(); } // If we were asked to print Clang stats, do so. if (opts.PrintClangStats && Context.getClangModuleLoader()) Context.getClangModuleLoader()->printStatistics(); if (!opts.DependenciesFilePath.empty()) (void)emitMakeDependencies(Context.Diags, *Instance->getDependencyTracker(), opts); if (shouldTrackReferences) emitReferenceDependencies(Context.Diags, Instance->getPrimarySourceFile(), *Instance->getDependencyTracker(), opts); if (Context.hadError()) return true; // FIXME: This is still a lousy approximation of whether the module file will // be externally consumed. bool moduleIsPublic = !Instance->getMainModule()->hasEntryPoint() && opts.ImplicitObjCHeaderPath.empty() && !Context.LangOpts.EnableAppExtensionRestrictions; // We've just been told to perform a typecheck, so we can return now. if (Action == FrontendOptions::Typecheck) { if (!opts.ObjCHeaderOutputPath.empty()) return printAsObjC(opts.ObjCHeaderOutputPath, Instance->getMainModule(), opts.ImplicitObjCHeaderPath, moduleIsPublic); return Context.hadError(); } assert(Action >= FrontendOptions::EmitSILGen && "All actions not requiring SILGen must have been handled!"); std::unique_ptr<SILModule> SM = Instance->takeSILModule(); if (!SM) { if (opts.PrimaryInput.hasValue() && opts.PrimaryInput.getValue().isFilename()) { FileUnit *PrimaryFile = PrimarySourceFile; if (!PrimaryFile) { auto Index = opts.PrimaryInput.getValue().Index; PrimaryFile = Instance->getMainModule()->getFiles()[Index]; } SM = performSILGeneration(*PrimaryFile, Invocation.getSILOptions(), None, opts.SILSerializeAll); } else { SM = performSILGeneration(Instance->getMainModule(), Invocation.getSILOptions(), opts.SILSerializeAll, true); } } if (observer) { observer->performedSILGeneration(*SM); } // We've been told to emit SIL after SILGen, so write it now. if (Action == FrontendOptions::EmitSILGen) { // If we are asked to link all, link all. if (Invocation.getSILOptions().LinkMode == SILOptions::LinkAll) performSILLinking(SM.get(), true); return writeSIL(*SM, Instance->getMainModule(), opts.EmitVerboseSIL, opts.getSingleOutputFilename(), opts.EmitSortedSIL); } if (Action == FrontendOptions::EmitSIBGen) { // If we are asked to link all, link all. if (Invocation.getSILOptions().LinkMode == SILOptions::LinkAll) performSILLinking(SM.get(), true); auto DC = PrimarySourceFile ? ModuleOrSourceFile(PrimarySourceFile) : Instance->getMainModule(); if (!opts.ModuleOutputPath.empty()) { SerializationOptions serializationOpts; serializationOpts.OutputPath = opts.ModuleOutputPath.c_str(); serializationOpts.SerializeAllSIL = true; serializationOpts.IsSIB = true; serialize(DC, serializationOpts, SM.get()); } return Context.hadError(); } // Perform "stable" optimizations that are invariant across compiler versions. if (!Invocation.getDiagnosticOptions().SkipDiagnosticPasses) { if (runSILDiagnosticPasses(*SM)) return true; if (observer) { observer->performedSILDiagnostics(*SM); } } else { // Even if we are not supposed to run the diagnostic passes, we still need // to run the ownership evaluator. if (runSILOwnershipEliminatorPass(*SM)) return true; } // Now if we are asked to link all, link all. if (Invocation.getSILOptions().LinkMode == SILOptions::LinkAll) performSILLinking(SM.get(), true); { SharedTimer timer("SIL verification (pre-optimization)"); SM->verify(); } // Perform SIL optimization passes if optimizations haven't been disabled. // These may change across compiler versions. { SharedTimer timer("SIL optimization"); if (Invocation.getSILOptions().Optimization > SILOptions::SILOptMode::None) { StringRef CustomPipelinePath = Invocation.getSILOptions().ExternalPassPipelineFilename; if (!CustomPipelinePath.empty()) { runSILOptimizationPassesWithFileSpecification(*SM, CustomPipelinePath); } else { runSILOptimizationPasses(*SM); } } else { runSILPassesForOnone(*SM); } } if (observer) { observer->performedSILOptimization(*SM); } { SharedTimer timer("SIL verification (post-optimization)"); SM->verify(); } // Gather instruction counts if we are asked to do so. if (SM->getOptions().PrintInstCounts) { performSILInstCount(&*SM); } // Get the main source file's private discriminator and attach it to // the compile unit's flags. if (PrimarySourceFile) { Identifier PD = PrimarySourceFile->getPrivateDiscriminator(); if (!PD.empty()) IRGenOpts.DWARFDebugFlags += (" -private-discriminator "+PD.str()).str(); } if (!opts.ObjCHeaderOutputPath.empty()) { (void)printAsObjC(opts.ObjCHeaderOutputPath, Instance->getMainModule(), opts.ImplicitObjCHeaderPath, moduleIsPublic); } if (Action == FrontendOptions::EmitSIB) { auto DC = PrimarySourceFile ? ModuleOrSourceFile(PrimarySourceFile) : Instance->getMainModule(); if (!opts.ModuleOutputPath.empty()) { SerializationOptions serializationOpts; serializationOpts.OutputPath = opts.ModuleOutputPath.c_str(); serializationOpts.SerializeAllSIL = true; serializationOpts.IsSIB = true; serialize(DC, serializationOpts, SM.get()); } return Context.hadError(); } if (!opts.ModuleOutputPath.empty() || !opts.ModuleDocOutputPath.empty()) { auto DC = PrimarySourceFile ? ModuleOrSourceFile(PrimarySourceFile) : Instance->getMainModule(); if (!opts.ModuleOutputPath.empty()) { SerializationOptions serializationOpts; serializationOpts.OutputPath = opts.ModuleOutputPath.c_str(); serializationOpts.DocOutputPath = opts.ModuleDocOutputPath.c_str(); serializationOpts.GroupInfoPath = opts.GroupInfoPath.c_str(); serializationOpts.SerializeAllSIL = opts.SILSerializeAll; if (opts.SerializeBridgingHeader) serializationOpts.ImportedHeader = opts.ImplicitObjCHeaderPath; serializationOpts.ModuleLinkName = opts.ModuleLinkName; serializationOpts.ExtraClangOptions = Invocation.getClangImporterOptions().ExtraArgs; if (!IRGenOpts.ForceLoadSymbolName.empty()) serializationOpts.AutolinkForceLoad = true; // Options contain information about the developer's computer, // so only serialize them if the module isn't going to be shipped to // the public. serializationOpts.SerializeOptionsForDebugging = !moduleIsPublic || opts.AlwaysSerializeDebuggingOptions; serialize(DC, serializationOpts, SM.get()); } if (Action == FrontendOptions::EmitModuleOnly) return Context.hadError(); } assert(Action >= FrontendOptions::EmitSIL && "All actions not requiring SILPasses must have been handled!"); // We've been told to write canonical SIL, so write it now. if (Action == FrontendOptions::EmitSIL) { return writeSIL(*SM, Instance->getMainModule(), opts.EmitVerboseSIL, opts.getSingleOutputFilename(), opts.EmitSortedSIL); } assert(Action >= FrontendOptions::Immediate && "All actions not requiring IRGen must have been handled!"); assert(Action != FrontendOptions::REPL && "REPL mode must be handled immediately after Instance->performSema()"); // Check if we had any errors; if we did, don't proceed to IRGen. if (Context.hadError()) return true; // Cleanup instructions/builtin calls not suitable for IRGen. performSILCleanup(SM.get()); // TODO: remove once the frontend understands what action it should perform IRGenOpts.OutputKind = getOutputKind(Action); if (Action == FrontendOptions::Immediate) { assert(!PrimarySourceFile && "-i doesn't work in -primary-file mode"); IRGenOpts.UseJIT = true; IRGenOpts.DebugInfoKind = IRGenDebugInfoKind::Normal; const ProcessCmdLine &CmdLine = ProcessCmdLine(opts.ImmediateArgv.begin(), opts.ImmediateArgv.end()); Instance->setSILModule(std::move(SM)); if (observer) { observer->aboutToRunImmediately(*Instance); } ReturnValue = RunImmediately(*Instance, CmdLine, IRGenOpts, Invocation.getSILOptions()); return Context.hadError(); } // FIXME: We shouldn't need to use the global context here, but // something is persisting across calls to performIRGeneration. auto &LLVMContext = getGlobalLLVMContext(); std::unique_ptr<llvm::Module> IRModule; llvm::GlobalVariable *HashGlobal; if (PrimarySourceFile) { IRModule = performIRGeneration(IRGenOpts, *PrimarySourceFile, std::move(SM), opts.getSingleOutputFilename(), LLVMContext, 0, &HashGlobal); } else { IRModule = performIRGeneration(IRGenOpts, Instance->getMainModule(), std::move(SM), opts.getSingleOutputFilename(), LLVMContext, &HashGlobal); } // Just because we had an AST error it doesn't mean we can't performLLVM. bool HadError = Instance->getASTContext().hadError(); // If the AST Context has no errors but no IRModule is available, // parallelIRGen happened correctly, since parallel IRGen produces multiple // modules. if (!IRModule) { return HadError; } std::unique_ptr<llvm::TargetMachine> TargetMachine = createTargetMachine(IRGenOpts, Context); version::Version EffectiveLanguageVersion = Context.LangOpts.EffectiveLanguageVersion; DiagnosticEngine &Diags = Context.Diags; const DiagnosticOptions &DiagOpts = Invocation.getDiagnosticOptions(); // Delete the compiler instance now that we have an IRModule. if (DiagOpts.VerifyMode == DiagnosticOptions::NoVerify) { SM.reset(); Instance.reset(); } // Now that we have a single IR Module, hand it over to performLLVM. return performLLVM(IRGenOpts, &Diags, nullptr, HashGlobal, IRModule.get(), TargetMachine.get(), EffectiveLanguageVersion, opts.getSingleOutputFilename()) || HadError; }