diff --git a/clang/include/clang/Basic/Diagnostic.h b/clang/include/clang/Basic/Diagnostic.h index a1683324b7ab..ffce99bf9f7d 100644 --- a/clang/include/clang/Basic/Diagnostic.h +++ b/clang/include/clang/Basic/Diagnostic.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -232,40 +233,97 @@ private: /// \brief Keeps and automatically disposes all DiagStates that we create. std::list DiagStates; - /// \brief Represents a point in source where the diagnostic state was - /// modified because of a pragma. - /// - /// 'Loc' can be null if the point represents the diagnostic state - /// modifications done through the command-line. - struct DiagStatePoint { - DiagState *State; - SourceLocation Loc; - DiagStatePoint(DiagState *State, SourceLocation Loc) - : State(State), Loc(Loc) { } + /// A mapping from files to the diagnostic states for those files. Lazily + /// built on demand for files in which the diagnostic state has not changed. + class DiagStateMap { + public: + /// Add an initial diagnostic state. + void appendFirst(DiagState *State); + /// Add a new latest state point. + void append(SourceManager &SrcMgr, SourceLocation Loc, DiagState *State); + /// Look up the diagnostic state at a given source location. + DiagState *lookup(SourceManager &SrcMgr, SourceLocation Loc) const; + /// Determine whether this map is empty. + bool empty() const { return Files.empty(); } + /// Clear out this map. + void clear() { + Files.clear(); + FirstDiagState = CurDiagState = nullptr; + CurDiagStateLoc = SourceLocation(); + } + + /// Grab the most-recently-added state point. + DiagState *getCurDiagState() const { return CurDiagState; } + /// Get the location at which a diagnostic state was last added. + SourceLocation getCurDiagStateLoc() const { return CurDiagStateLoc; } + + private: + /// \brief Represents a point in source where the diagnostic state was + /// modified because of a pragma. + /// + /// 'Loc' can be null if the point represents the diagnostic state + /// modifications done through the command-line. + struct DiagStatePoint { + DiagState *State; + unsigned Offset; + DiagStatePoint(DiagState *State, unsigned Offset) + : State(State), Offset(Offset) { } + }; + + /// Description of the diagnostic states and state transitions for a + /// particular FileID. + struct File { + /// The diagnostic state for the parent file. This is strictly redundant, + /// as looking up the DecomposedIncludedLoc for the FileID in the Files + /// map would give us this, but we cache it here for performance. + File *Parent = nullptr; + /// The offset of this file within its parent. + unsigned ParentOffset = 0; + /// Whether this file has any local (not imported from an AST file) + /// diagnostic state transitions. + bool HasLocalTransitions = false; + /// The points within the file where the state changes. There will always + /// be at least one of these (the state on entry to the file). + llvm::SmallVector StateTransitions; + + DiagState *lookup(unsigned Offset) const; + }; + + /// The diagnostic states for each file. + mutable std::map Files; + + /// The initial diagnostic state. + DiagState *FirstDiagState; + /// The current diagnostic state. + DiagState *CurDiagState; + /// The location at which the current diagnostic state was established. + SourceLocation CurDiagStateLoc; + + /// Get the diagnostic state information for a file. + File *getFile(SourceManager &SrcMgr, FileID ID) const; + + friend class ASTReader; + friend class ASTWriter; }; - /// \brief A sorted vector of all DiagStatePoints representing changes in - /// diagnostic state due to diagnostic pragmas. - /// - /// The vector is always sorted according to the SourceLocation of the - /// DiagStatePoint. - typedef std::vector DiagStatePointsTy; - mutable DiagStatePointsTy DiagStatePoints; + DiagStateMap DiagStatesByLoc; /// \brief Keeps the DiagState that was active during each diagnostic 'push' /// so we can get back at it when we 'pop'. std::vector DiagStateOnPushStack; DiagState *GetCurDiagState() const { - assert(!DiagStatePoints.empty()); - return DiagStatePoints.back().State; + return DiagStatesByLoc.getCurDiagState(); } void PushDiagStatePoint(DiagState *State, SourceLocation L); /// \brief Finds the DiagStatePoint that contains the diagnostic state of /// the given source location. - DiagStatePointsTy::iterator GetDiagStatePointForLoc(SourceLocation Loc) const; + DiagState *GetDiagStateForLoc(SourceLocation Loc) const { + return SourceMgr ? DiagStatesByLoc.lookup(*SourceMgr, Loc) + : DiagStatesByLoc.getCurDiagState(); + } /// \brief Sticky flag set to \c true when an error is emitted. bool ErrorOccurred; @@ -372,7 +430,7 @@ public: return *SourceMgr; } void setSourceManager(SourceManager *SrcMgr) { - assert(DiagStatePoints.size() == 1 && DiagStatePoints[0].Loc.isInvalid() && + assert(DiagStatesByLoc.empty() && "Leftover diag state from a different SourceManager."); SourceMgr = SrcMgr; } diff --git a/clang/lib/Basic/Diagnostic.cpp b/clang/lib/Basic/Diagnostic.cpp index 89b704da0114..0c27ae4638d5 100644 --- a/clang/lib/Basic/Diagnostic.cpp +++ b/clang/lib/Basic/Diagnostic.cpp @@ -132,13 +132,13 @@ void DiagnosticsEngine::Reset() { // Clear state related to #pragma diagnostic. DiagStates.clear(); - DiagStatePoints.clear(); + DiagStatesByLoc.clear(); DiagStateOnPushStack.clear(); // Create a DiagState and DiagStatePoint representing diagnostic changes // through command-line. DiagStates.emplace_back(); - DiagStatePoints.emplace_back(&DiagStates.back(), SourceLocation()); + DiagStatesByLoc.appendFirst(&DiagStates.back()); } void DiagnosticsEngine::SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1, @@ -158,49 +158,94 @@ void DiagnosticsEngine::ReportDelayed() { DelayedDiagArg2.clear(); } -void DiagnosticsEngine::PushDiagStatePoint(DiagState *State, - SourceLocation Loc) { - // Make sure that DiagStatePoints is always sorted according to Loc. - assert(Loc.isValid() && "Adding invalid loc point"); - assert(!DiagStatePoints.empty() && - (DiagStatePoints.back().Loc.isInvalid() || - getSourceManager().isBeforeInTranslationUnit( - DiagStatePoints.back().Loc, Loc)) && - "Previous point loc comes after or is the same as new one"); - DiagStatePoints.push_back(DiagStatePoint(State, Loc)); +void DiagnosticsEngine::DiagStateMap::appendFirst( + DiagState *State) { + assert(Files.empty() && "not first"); + FirstDiagState = CurDiagState = State; + CurDiagStateLoc = SourceLocation(); } -DiagnosticsEngine::DiagStatePointsTy::iterator -DiagnosticsEngine::GetDiagStatePointForLoc(SourceLocation L) const { - assert(!DiagStatePoints.empty()); - assert(DiagStatePoints.front().Loc.isInvalid() && - "Should have created a DiagStatePoint for command-line"); +void DiagnosticsEngine::DiagStateMap::append(SourceManager &SrcMgr, + SourceLocation Loc, + DiagState *State) { + CurDiagState = State; + CurDiagStateLoc = Loc; - if (!SourceMgr) - return DiagStatePoints.end() - 1; + std::pair Decomp = SrcMgr.getDecomposedLoc(Loc); + unsigned Offset = Decomp.second; + for (File *F = getFile(SrcMgr, Decomp.first); F; + Offset = F->ParentOffset, F = F->Parent) { + F->HasLocalTransitions = true; + auto &Last = F->StateTransitions.back(); + assert(Last.Offset <= Offset && "state transitions added out of order"); - FullSourceLoc Loc(L, *SourceMgr); - if (Loc.isInvalid()) - return DiagStatePoints.end() - 1; + if (Last.Offset == Offset) { + if (Last.State == State) + break; + Last.State = State; + continue; + } - DiagStatePointsTy::iterator Pos = DiagStatePoints.end(); - SourceLocation LastStateChangePos = DiagStatePoints.back().Loc; - if (LastStateChangePos.isValid() && - Loc.isBeforeInTranslationUnitThan(LastStateChangePos)) - Pos = std::upper_bound( - DiagStatePoints.begin(), DiagStatePoints.end(), - DiagStatePoint(nullptr, Loc), - [this](const DiagStatePoint &LHS, const DiagStatePoint &RHS) { - // If Loc is invalid it means it came from , in which - // case we regard it as coming before any valid source location. - if (RHS.Loc.isInvalid()) - return false; - if (LHS.Loc.isInvalid()) - return true; - return SourceMgr->isBeforeInTranslationUnit(LHS.Loc, RHS.Loc); - }); - --Pos; - return Pos; + F->StateTransitions.push_back({State, Offset}); + } +} + +DiagnosticsEngine::DiagState * +DiagnosticsEngine::DiagStateMap::lookup(SourceManager &SrcMgr, + SourceLocation Loc) const { + // Common case: we have not seen any diagnostic pragmas. + if (Files.empty()) + return FirstDiagState; + + std::pair Decomp = SrcMgr.getDecomposedLoc(Loc); + const File *F = getFile(SrcMgr, Decomp.first); + return F->lookup(Decomp.second); +} + +DiagnosticsEngine::DiagState * +DiagnosticsEngine::DiagStateMap::File::lookup(unsigned Offset) const { + auto OnePastIt = std::upper_bound( + StateTransitions.begin(), StateTransitions.end(), Offset, + [](unsigned Offset, const DiagStatePoint &P) { + return Offset < P.Offset; + }); + assert(OnePastIt != StateTransitions.begin() && "missing initial state"); + return OnePastIt[-1].State; +} + +DiagnosticsEngine::DiagStateMap::File * +DiagnosticsEngine::DiagStateMap::getFile(SourceManager &SrcMgr, + FileID ID) const { + // Get or insert the File for this ID. + auto Range = Files.equal_range(ID); + if (Range.first != Range.second) + return &Range.first->second; + auto &F = Files.insert(Range.first, std::make_pair(ID, File()))->second; + + // We created a new File; look up the diagnostic state at the start of it and + // initialize it. + if (ID.isValid()) { + std::pair Decomp = SrcMgr.getDecomposedIncludedLoc(ID); + F.Parent = getFile(SrcMgr, Decomp.first); + F.ParentOffset = Decomp.second; + F.StateTransitions.push_back({F.Parent->lookup(Decomp.second), 0}); + } else { + // This is the (imaginary) root file into which we pretend all top-level + // files are included; it descends from the initial state. + // + // FIXME: This doesn't guarantee that we use the same ordering as + // isBeforeInTranslationUnit in the cases where someone invented another + // top-level file and added diagnostic pragmas to it. See the code at the + // end of isBeforeInTranslationUnit for the quirks it deals with. + F.StateTransitions.push_back({FirstDiagState, 0}); + } + return &F; +} + +void DiagnosticsEngine::PushDiagStatePoint(DiagState *State, + SourceLocation Loc) { + assert(Loc.isValid() && "Adding invalid loc point"); + DiagStatesByLoc.append(*SourceMgr, Loc, State); } void DiagnosticsEngine::setSeverity(diag::kind Diag, diag::Severity Map, @@ -210,11 +255,8 @@ void DiagnosticsEngine::setSeverity(diag::kind Diag, diag::Severity Map, assert((Diags->isBuiltinWarningOrExtension(Diag) || (Map == diag::Severity::Fatal || Map == diag::Severity::Error)) && "Cannot map errors into warnings!"); - assert(!DiagStatePoints.empty()); assert((L.isInvalid() || SourceMgr) && "No SourceMgr for valid location"); - SourceLocation Loc = SourceMgr ? L : SourceLocation(); - SourceLocation LastStateChangePos = DiagStatePoints.back().Loc; // Don't allow a mapping to a warning override an error/fatal mapping. if (Map == diag::Severity::Warning) { DiagnosticMapping &Info = GetCurDiagState()->getOrAddMapping(Diag); @@ -225,49 +267,22 @@ void DiagnosticsEngine::setSeverity(diag::kind Diag, diag::Severity Map, DiagnosticMapping Mapping = makeUserMapping(Map, L); // Common case; setting all the diagnostics of a group in one place. - if (Loc.isInvalid() || Loc == LastStateChangePos) { - GetCurDiagState()->setMapping(Diag, Mapping); + if ((L.isInvalid() || L == DiagStatesByLoc.getCurDiagStateLoc()) && + DiagStatesByLoc.getCurDiagState()) { + // FIXME: This is theoretically wrong: if the current state is shared with + // some other location (via push/pop) we will change the state for that + // other location as well. This cannot currently happen, as we can't update + // the diagnostic state at the same location at which we pop. + DiagStatesByLoc.getCurDiagState()->setMapping(Diag, Mapping); return; } - // Another common case; modifying diagnostic state in a source location - // after the previous one. - if ((Loc.isValid() && LastStateChangePos.isInvalid()) || - SourceMgr->isBeforeInTranslationUnit(LastStateChangePos, Loc)) { - // A diagnostic pragma occurred, create a new DiagState initialized with - // the current one and a new DiagStatePoint to record at which location - // the new state became active. - DiagStates.push_back(*GetCurDiagState()); - PushDiagStatePoint(&DiagStates.back(), Loc); - GetCurDiagState()->setMapping(Diag, Mapping); - return; - } - - // We allow setting the diagnostic state in random source order for - // completeness but it should not be actually happening in normal practice. - - DiagStatePointsTy::iterator Pos = GetDiagStatePointForLoc(Loc); - assert(Pos != DiagStatePoints.end()); - - // Update all diagnostic states that are active after the given location. - for (DiagStatePointsTy::iterator - I = Pos+1, E = DiagStatePoints.end(); I != E; ++I) { - I->State->setMapping(Diag, Mapping); - } - - // If the location corresponds to an existing point, just update its state. - if (Pos->Loc == Loc) { - Pos->State->setMapping(Diag, Mapping); - return; - } - - // Create a new state/point and fit it into the vector of DiagStatePoints - // so that the vector is always ordered according to location. - assert(SourceMgr->isBeforeInTranslationUnit(Pos->Loc, Loc)); - DiagStates.push_back(*Pos->State); - DiagState *NewState = &DiagStates.back(); - NewState->setMapping(Diag, Mapping); - DiagStatePoints.insert(Pos + 1, DiagStatePoint(NewState, Loc)); + // A diagnostic pragma occurred, create a new DiagState initialized with + // the current one and a new DiagStatePoint to record at which location + // the new state became active. + DiagStates.push_back(*GetCurDiagState()); + DiagStates.back().setMapping(Diag, Mapping); + PushDiagStatePoint(&DiagStates.back(), L); } bool DiagnosticsEngine::setSeverityForGroup(diag::Flavor Flavor, diff --git a/clang/lib/Basic/DiagnosticIDs.cpp b/clang/lib/Basic/DiagnosticIDs.cpp index 3c370f67fa32..e0580af45b50 100644 --- a/clang/lib/Basic/DiagnosticIDs.cpp +++ b/clang/lib/Basic/DiagnosticIDs.cpp @@ -411,11 +411,8 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc, // to error. Errors can only be mapped to fatal. diag::Severity Result = diag::Severity::Fatal; - DiagnosticsEngine::DiagStatePointsTy::iterator - Pos = Diag.GetDiagStatePointForLoc(Loc); - DiagnosticsEngine::DiagState *State = Pos->State; - // Get the mapping information, or compute it lazily. + DiagnosticsEngine::DiagState *State = Diag.GetDiagStateForLoc(Loc); DiagnosticMapping &Mapping = State->getOrAddMapping((diag::kind)DiagID); // TODO: Can a null severity really get here? diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index a4da0d2ebe2b..25a9b1081c21 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -5297,48 +5297,84 @@ HeaderFileInfo ASTReader::GetHeaderFileInfo(const FileEntry *FE) { } void ASTReader::ReadPragmaDiagnosticMappings(DiagnosticsEngine &Diag) { - // FIXME: Make it work properly with modules. - SmallVector DiagStates; + using DiagState = DiagnosticsEngine::DiagState; + SmallVector DiagStates; + for (ModuleIterator I = ModuleMgr.begin(), E = ModuleMgr.end(); I != E; ++I) { ModuleFile &F = *(*I); unsigned Idx = 0; - DiagStates.clear(); - assert(!Diag.DiagStates.empty()); - DiagStates.push_back(&Diag.DiagStates.front()); // the command-line one. - while (Idx < F.PragmaDiagMappings.size()) { - SourceLocation Loc = ReadSourceLocation(F, F.PragmaDiagMappings[Idx++]); - unsigned DiagStateID = F.PragmaDiagMappings[Idx++]; - if (DiagStateID != 0) { - Diag.DiagStatePoints.push_back( - DiagnosticsEngine::DiagStatePoint(DiagStates[DiagStateID-1], - FullSourceLoc(Loc, SourceMgr))); - continue; - } + auto &Record = F.PragmaDiagMappings; + if (Record.empty()) + continue; + + DiagStates.clear(); + + auto ReadDiagState = + [&](const DiagState &BasedOn, SourceLocation Loc, + bool IncludeNonPragmaStates) -> DiagnosticsEngine::DiagState * { + unsigned BackrefID = Record[Idx++]; + if (BackrefID != 0) + return DiagStates[BackrefID - 1]; - assert(DiagStateID == 0); // A new DiagState was created here. - Diag.DiagStates.push_back(*Diag.GetCurDiagState()); - DiagnosticsEngine::DiagState *NewState = &Diag.DiagStates.back(); + Diag.DiagStates.push_back(BasedOn); + DiagState *NewState = &Diag.DiagStates.back(); DiagStates.push_back(NewState); - Diag.DiagStatePoints.push_back( - DiagnosticsEngine::DiagStatePoint(NewState, - FullSourceLoc(Loc, SourceMgr))); - while (true) { - assert(Idx < F.PragmaDiagMappings.size() && - "Invalid data, didn't find '-1' marking end of diag/map pairs"); - if (Idx >= F.PragmaDiagMappings.size()) { - break; // Something is messed up but at least avoid infinite loop in - // release build. - } - unsigned DiagID = F.PragmaDiagMappings[Idx++]; - if (DiagID == (unsigned)-1) { - break; // no more diag/map pairs for this location. - } - diag::Severity Map = (diag::Severity)F.PragmaDiagMappings[Idx++]; + while (Idx + 1 < Record.size() && Record[Idx] != unsigned(-1)) { + unsigned DiagID = Record[Idx++]; + diag::Severity Map = (diag::Severity)Record[Idx++]; DiagnosticMapping Mapping = Diag.makeUserMapping(Map, Loc); - Diag.GetCurDiagState()->setMapping(DiagID, Mapping); + if (Mapping.isPragma() || IncludeNonPragmaStates) + NewState->setMapping(DiagID, Mapping); + } + assert(Idx != Record.size() && Record[Idx] == unsigned(-1) && + "Invalid data, didn't find '-1' marking end of diag/map pairs"); + ++Idx; + return NewState; + }; + + auto *FirstState = ReadDiagState( + F.isModule() ? DiagState() : *Diag.DiagStatesByLoc.CurDiagState, + SourceLocation(), F.isModule()); + SourceLocation CurStateLoc = + ReadSourceLocation(F, F.PragmaDiagMappings[Idx++]); + auto *CurState = ReadDiagState(*FirstState, CurStateLoc, false); + + if (!F.isModule()) { + Diag.DiagStatesByLoc.CurDiagState = CurState; + Diag.DiagStatesByLoc.CurDiagStateLoc = CurStateLoc; + + // Preserve the property that the imaginary root file describes the + // current state. + auto &T = Diag.DiagStatesByLoc.Files[FileID()].StateTransitions; + if (T.empty()) + T.push_back({CurState, 0}); + else + T[0].State = CurState; + } + + while (Idx < Record.size()) { + SourceLocation Loc = ReadSourceLocation(F, Record[Idx++]); + auto IDAndOffset = SourceMgr.getDecomposedLoc(Loc); + assert(IDAndOffset.second == 0 && "not a start location for a FileID"); + unsigned Transitions = Record[Idx++]; + + // Note that we don't need to set up Parent/ParentOffset here, because + // we won't be changing the diagnostic state within imported FileIDs + // (other than perhaps appending to the main source file, which has no + // parent). + auto &F = Diag.DiagStatesByLoc.Files[IDAndOffset.first]; + F.StateTransitions.reserve(F.StateTransitions.size() + Transitions); + for (unsigned I = 0; I != Transitions; ++I) { + unsigned Offset = Record[Idx++]; + auto *State = + ReadDiagState(*FirstState, Loc.getLocWithOffset(Offset), false); + F.StateTransitions.push_back({State, Offset}); } } + + // Don't try to read these mappings again. + Record.clear(); } } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 4400585fd4e1..b2237a5fc196 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -2802,38 +2802,43 @@ ASTWriter::inferSubmoduleIDFromLocation(SourceLocation Loc) { void ASTWriter::WritePragmaDiagnosticMappings(const DiagnosticsEngine &Diag, bool isModule) { - // Make sure set diagnostic pragmas don't affect the translation unit that - // imports the module. - // FIXME: Make diagnostic pragma sections work properly with modules. - if (isModule) - return; - llvm::SmallDenseMap DiagStateIDMap; unsigned CurrID = 0; - DiagStateIDMap[&Diag.DiagStates.front()] = ++CurrID; // the command-line one. RecordData Record; - for (DiagnosticsEngine::DiagStatePointsTy::const_iterator - I = Diag.DiagStatePoints.begin(), E = Diag.DiagStatePoints.end(); - I != E; ++I) { - const DiagnosticsEngine::DiagStatePoint &point = *I; - if (point.Loc.isInvalid()) - continue; - AddSourceLocation(point.Loc, Record); - unsigned &DiagStateID = DiagStateIDMap[point.State]; + auto AddDiagState = [&](const DiagnosticsEngine::DiagState *State, + bool IncludeNonPragmaStates) { + unsigned &DiagStateID = DiagStateIDMap[State]; Record.push_back(DiagStateID); - + if (DiagStateID == 0) { DiagStateID = ++CurrID; - for (const auto &I : *(point.State)) { - if (I.second.isPragma()) { + for (const auto &I : *State) { + if (I.second.isPragma() || IncludeNonPragmaStates) { Record.push_back(I.first); Record.push_back((unsigned)I.second.getSeverity()); } } - Record.push_back(-1); // mark the end of the diag/map pairs for this - // location. + // Add a sentinel to mark the end of the diag IDs. + Record.push_back(unsigned(-1)); + } + }; + + AddDiagState(Diag.DiagStatesByLoc.FirstDiagState, isModule); + AddSourceLocation(Diag.DiagStatesByLoc.CurDiagStateLoc, Record); + AddDiagState(Diag.DiagStatesByLoc.CurDiagState, false); + + for (auto &FileIDAndFile : Diag.DiagStatesByLoc.Files) { + if (!FileIDAndFile.first.isValid() || + !FileIDAndFile.second.HasLocalTransitions) + continue; + AddSourceLocation(Diag.SourceMgr->getLocForStartOfFile(FileIDAndFile.first), + Record); + Record.push_back(FileIDAndFile.second.StateTransitions.size()); + for (auto &StatePoint : FileIDAndFile.second.StateTransitions) { + Record.push_back(StatePoint.Offset); + AddDiagState(StatePoint.State, false); } } diff --git a/clang/test/Modules/Inputs/diag_pragma.h b/clang/test/Modules/Inputs/diag_pragma.h index a8f958994ca5..59c73ea756e0 100644 --- a/clang/test/Modules/Inputs/diag_pragma.h +++ b/clang/test/Modules/Inputs/diag_pragma.h @@ -1,3 +1,13 @@ #define DIAG_PRAGMA_MACRO 1 #pragma clang diagnostic ignored "-Wparentheses" + +#ifdef __cplusplus +template const char *f(T t) { + return "foo" + t; +} +#pragma clang diagnostic ignored "-Wstring-plus-int" +template const char *g(T t) { + return "foo" + t; +} +#endif diff --git a/clang/test/Modules/diag-pragma.cpp b/clang/test/Modules/diag-pragma.cpp new file mode 100644 index 000000000000..347401fffc20 --- /dev/null +++ b/clang/test/Modules/diag-pragma.cpp @@ -0,0 +1,47 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -emit-module -fmodules-cache-path=%t -fmodule-name=diag_pragma -x c++ %S/Inputs/module.map -fmodules-ts +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -emit-module -fmodule-name=diag_pragma -x c++ %S/Inputs/module.map -fmodules-ts -o %t/explicit.pcm -Werror=string-plus-int +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -verify -fmodules-cache-path=%t -I %S/Inputs %s -fmodules-ts -DEXPLICIT_FLAG -fmodule-file=%t/explicit.pcm + +import diag_pragma; + +int foo(int x) { + // Diagnostics from templates in the module follow the diagnostic state from + // when the module was built. +#ifdef EXPLICIT_FLAG + // expected-error@diag_pragma.h:7 {{adding 'int' to a string}} +#else + // expected-warning@diag_pragma.h:7 {{adding 'int' to a string}} +#endif + // expected-note@diag_pragma.h:7 {{use array indexing}} + f(0); // expected-note {{instantiation of}} + + g(0); // ok, warning was ignored when building module + + // Diagnostics from this source file ignore the diagnostic state from the + // module. + void("foo" + x); // expected-warning {{adding 'int' to a string}} + // expected-note@-1 {{use array indexing}} + +#pragma clang diagnostic ignored "-Wstring-plus-int" + + // Diagnostics from the module ignore diagnostic state changes from this + // source file. +#ifdef EXPLICIT_FLAG + // expected-error@diag_pragma.h:7 {{adding 'long' to a string}} +#else + // expected-warning@diag_pragma.h:7 {{adding 'long' to a string}} +#endif + // expected-note@diag_pragma.h:7 {{use array indexing}} + f(0L); // expected-note {{instantiation of}} + + g(0L); + + void("bar" + x); + + if (x = DIAG_PRAGMA_MACRO) // expected-warning {{using the result of an assignment as a condition without parentheses}} \ + // expected-note {{place parentheses}} expected-note {{use '=='}} + return 0; + return 1; +}