aboutsummaryrefslogtreecommitdiff
path: root/include/clang
diff options
context:
space:
mode:
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>2010-12-15 18:44:22 +0000
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>2010-12-15 18:44:22 +0000
commit0827408865e32789e0ec4b8113a302ccdc531423 (patch)
treed8c9122a107aab93c08b01adb6df4f9c954c3705 /include/clang
parentc46333550f5787b6d48ca3043e14ba9594cb632d (diff)
Fix diagnostic pragmas.
Diagnostic pragmas are broken because we don't keep track of the diagnostic state changes and we only check the current/latest state. Problems manifest if a diagnostic is emitted for a source line that has different diagnostic state than the current state; this can affect a lot of places, like C++ inline methods, template instantiations, the lexer, etc. Fix the issue by having the Diagnostic object keep track of the source location of the pragmas so that it is able to know what is the diagnostic state at any given source location. Fixes rdar://8365684. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@121873 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'include/clang')
-rw-r--r--include/clang/Basic/Diagnostic.h121
-rw-r--r--include/clang/Basic/DiagnosticIDs.h11
-rw-r--r--include/clang/Basic/SourceLocation.h28
-rw-r--r--include/clang/Lex/MacroInfo.h13
-rw-r--r--include/clang/Lex/Preprocessor.h15
5 files changed, 154 insertions, 34 deletions
diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h
index b0ed3e1a17..bc835019c9 100644
--- a/include/clang/Basic/Diagnostic.h
+++ b/include/clang/Basic/Diagnostic.h
@@ -16,11 +16,13 @@
#include "clang/Basic/DiagnosticIDs.h"
#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/type_traits.h"
#include <vector>
+#include <list>
namespace clang {
class DiagnosticClient;
@@ -158,30 +160,82 @@ private:
DiagnosticClient *Client;
bool OwnsDiagClient;
SourceManager *SourceMgr;
-
- /// DiagMappings - Mapping information for diagnostics. Mapping info is
+
+ /// \brief Mapping information for diagnostics. Mapping info is
/// packed into four bits per diagnostic. The low three bits are the mapping
/// (an instance of diag::Mapping), or zero if unset. The high bit is set
/// when the mapping was established as a user mapping. If the high bit is
/// clear, then the low bits are set to the default value, and should be
/// mapped with -pedantic, -Werror, etc.
- class DiagMappings {
- unsigned char Values[diag::DIAG_UPPER_LIMIT/2];
+ ///
+ /// Contrary to DiagMappings, a new DiagState is created and kept around when
+ /// diagnostic pragmas modify the state so that we know what is the diagnostic
+ /// state at any given source location.
+ class DiagState {
+ mutable llvm::DenseMap<unsigned, unsigned> DiagMap;
public:
- DiagMappings() : Values() /*zero-initialization of array*/ { }
+ void setMapping(diag::kind Diag, unsigned Map) { DiagMap[Diag] = Map; }
- void setMapping(diag::kind Diag, unsigned Map) {
- size_t Shift = (Diag & 1)*4;
- Values[Diag/2] = (Values[Diag/2] & ~(15 << Shift)) | (Map << Shift);
+ diag::Mapping getMapping(diag::kind Diag) const {
+ return (diag::Mapping)DiagMap[Diag];
}
+ };
- diag::Mapping getMapping(diag::kind Diag) const {
- return (diag::Mapping)((Values[Diag/2] >> (Diag & 1)*4) & 15);
+ /// \brief Keeps and automatically disposes all DiagStates that we create.
+ std::list<DiagState> 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;
+ FullSourceLoc Loc;
+ DiagStatePoint(DiagState *State, FullSourceLoc Loc)
+ : State(State), Loc(Loc) { }
+
+ bool operator<(const DiagStatePoint &RHS) const {
+ // If Loc is invalid it means it came from <command-line>, in which case
+ // we regard it as coming before any valid source location.
+ if (RHS.Loc.isInvalid())
+ return false;
+ if (Loc.isInvalid())
+ return true;
+ return Loc.isBeforeInTranslationUnitThan(RHS.Loc);
}
};
- mutable std::vector<DiagMappings> DiagMappingsStack;
+ /// \brief A 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<DiagStatePoint> DiagStatePointsTy;
+ mutable DiagStatePointsTy DiagStatePoints;
+
+ /// \brief Keeps the DiagState that was active during each diagnostic 'push'
+ /// so we can get back at it when we 'pop'.
+ std::vector<DiagState *> DiagStateOnPushStack;
+
+ DiagState *GetCurDiagState() const {
+ assert(!DiagStatePoints.empty());
+ return DiagStatePoints.back().State;
+ }
+
+ void PushDiagStatePoint(DiagState *State, SourceLocation L) {
+ FullSourceLoc Loc(L, *SourceMgr);
+ // Make sure that DiagStatePoints is always sorted according to Loc.
+ assert((Loc.isValid() || DiagStatePoints.empty()) &&
+ "Adding invalid loc point after another point");
+ assert((Loc.isInvalid() || DiagStatePoints.empty() ||
+ DiagStatePoints.back().Loc.isInvalid() ||
+ DiagStatePoints.back().Loc.isBeforeInTranslationUnitThan(Loc)) &&
+ "Previous point loc comes after or is the same as new one");
+ DiagStatePoints.push_back(DiagStatePoint(State,
+ FullSourceLoc(Loc, *SourceMgr)));
+ }
+
+ /// \brief Finds the DiagStatePoint that contains the diagnostic state of
+ /// the given source location.
+ DiagStatePointsTy::iterator GetDiagStatePointForLoc(SourceLocation Loc) const;
/// ErrorOccurred / FatalErrorOccurred - This is set to true when an error or
/// fatal error is emitted, and is sticky.
@@ -261,13 +315,13 @@ public:
/// pushMappings - Copies the current DiagMappings and pushes the new copy
/// onto the top of the stack.
- void pushMappings();
+ void pushMappings(SourceLocation Loc);
/// popMappings - Pops the current DiagMappings off the top of the stack
/// causing the new top of the stack to be the active mappings. Returns
/// true if the pop happens, false if there is only one DiagMapping on the
/// stack.
- bool popMappings();
+ bool popMappings(SourceLocation Loc);
/// \brief Set the diagnostic client associated with this diagnostic object.
///
@@ -349,23 +403,24 @@ public:
void DecrementAllExtensionsSilenced() { --AllExtensionsSilenced; }
bool hasAllExtensionsSilenced() { return AllExtensionsSilenced != 0; }
- /// setDiagnosticMapping - This allows the client to specify that certain
+ /// \brief This allows the client to specify that certain
/// warnings are ignored. Notes can never be mapped, errors can only be
/// mapped to fatal, and WARNINGs and EXTENSIONs can be mapped arbitrarily.
- void setDiagnosticMapping(diag::kind Diag, diag::Mapping Map) {
- assert(Diag < diag::DIAG_UPPER_LIMIT &&
- "Can only map builtin diagnostics");
- assert((Diags->isBuiltinWarningOrExtension(Diag) ||
- (Map == diag::MAP_FATAL || Map == diag::MAP_ERROR)) &&
- "Cannot map errors into warnings!");
- setDiagnosticMappingInternal(Diag, Map, true);
- }
+ ///
+ /// \param Loc The source location that this change of diagnostic state should
+ /// take affect. It can be null if we are setting the latest state.
+ void setDiagnosticMapping(diag::kind Diag, diag::Mapping Map,
+ SourceLocation Loc);
/// setDiagnosticGroupMapping - Change an entire diagnostic group (e.g.
/// "unknown-pragmas" to have the specified mapping. This returns true and
/// ignores the request if "Group" was unknown, false otherwise.
- bool setDiagnosticGroupMapping(const char *Group, diag::Mapping Map) {
- return Diags->setDiagnosticGroupMapping(Group, Map, *this);
+ ///
+ /// 'Loc' is the source location that this change of diagnostic state should
+ /// take affect. It can be null if we are setting the state from command-line.
+ bool setDiagnosticGroupMapping(const char *Group, diag::Mapping Map,
+ SourceLocation Loc = SourceLocation()) {
+ return Diags->setDiagnosticGroupMapping(Group, Map, Loc, *this);
}
bool hasErrorOccurred() const { return ErrorOccurred; }
@@ -408,11 +463,14 @@ public:
// Diagnostic classification and reporting interfaces.
//
- /// getDiagnosticLevel - Based on the way the client configured the Diagnostic
+ /// \brief Based on the way the client configured the Diagnostic
/// object, classify the specified diagnostic ID into a Level, consumable by
/// the DiagnosticClient.
- Level getDiagnosticLevel(unsigned DiagID) const {
- return (Level)Diags->getDiagnosticLevel(DiagID, *this);
+ ///
+ /// \param Loc The source location we are interested in finding out the
+ /// diagnostic state. Can be null in order to query the latest state.
+ Level getDiagnosticLevel(unsigned DiagID, SourceLocation Loc) const {
+ return (Level)Diags->getDiagnosticLevel(DiagID, Loc, *this);
}
/// Report - Issue the message to the client. @c DiagID is a member of the
@@ -461,14 +519,15 @@ private:
/// getDiagnosticMappingInfo - Return the mapping info currently set for the
/// specified builtin diagnostic. This returns the high bit encoding, or zero
/// if the field is completely uninitialized.
- diag::Mapping getDiagnosticMappingInfo(diag::kind Diag) const {
- return DiagMappingsStack.back().getMapping(Diag);
+ diag::Mapping getDiagnosticMappingInfo(diag::kind Diag,
+ DiagState *State) const {
+ return State->getMapping(Diag);
}
void setDiagnosticMappingInternal(unsigned DiagId, unsigned Map,
- bool isUser) const {
+ DiagState *State, bool isUser) const {
if (isUser) Map |= 8; // Set the high bit for user mappings.
- DiagMappingsStack.back().setMapping((diag::kind)DiagId, Map);
+ State->setMapping((diag::kind)DiagId, Map);
}
// This is private state used by DiagnosticBuilder. We put it here instead of
diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h
index a84ebfaf9f..eede94b2c5 100644
--- a/include/clang/Basic/DiagnosticIDs.h
+++ b/include/clang/Basic/DiagnosticIDs.h
@@ -19,6 +19,7 @@
namespace clang {
class Diagnostic;
+ class SourceLocation;
// Import the diagnostic enums themselves.
namespace diag {
@@ -174,18 +175,22 @@ private:
/// "unknown-pragmas" to have the specified mapping. This returns true and
/// ignores the request if "Group" was unknown, false otherwise.
bool setDiagnosticGroupMapping(const char *Group, diag::Mapping Map,
- Diagnostic &Diag) const;
+ SourceLocation Loc, Diagnostic &Diag) const;
- /// getDiagnosticLevel - Based on the way the client configured the Diagnostic
+ /// \brief Based on the way the client configured the Diagnostic
/// object, classify the specified diagnostic ID into a Level, consumable by
/// the DiagnosticClient.
- DiagnosticIDs::Level getDiagnosticLevel(unsigned DiagID,
+ ///
+ /// \param Loc The source location we are interested in finding out the
+ /// diagnostic state. Can be null in order to query the latest state.
+ DiagnosticIDs::Level getDiagnosticLevel(unsigned DiagID, SourceLocation Loc,
const Diagnostic &Diag) const;
/// getDiagnosticLevel - This is an internal implementation helper used when
/// DiagClass is already known.
DiagnosticIDs::Level getDiagnosticLevel(unsigned DiagID,
unsigned DiagClass,
+ SourceLocation Loc,
const Diagnostic &Diag) const;
/// ProcessDiag - This is the method used to report a diagnostic that is
diff --git a/include/clang/Basic/SourceLocation.h b/include/clang/Basic/SourceLocation.h
index da2ec15e3f..74f762bd90 100644
--- a/include/clang/Basic/SourceLocation.h
+++ b/include/clang/Basic/SourceLocation.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_SOURCELOCATION_H
#define LLVM_CLANG_SOURCELOCATION_H
+#include "llvm/Support/PointerLikeTypeTraits.h"
#include <utility>
#include <cassert>
@@ -264,6 +265,20 @@ public:
bool isInSystemHeader() const;
+ /// \brief Determines the order of 2 source locations in the translation unit.
+ ///
+ /// \returns true if this source location comes before 'Loc', false otherwise.
+ bool isBeforeInTranslationUnitThan(SourceLocation Loc) const;
+
+ /// \brief Determines the order of 2 source locations in the translation unit.
+ ///
+ /// \returns true if this source location comes before 'Loc', false otherwise.
+ bool isBeforeInTranslationUnitThan(FullSourceLoc Loc) const {
+ assert(Loc.isValid());
+ assert(SrcMgr == Loc.SrcMgr && "Loc comes from another SourceManager!");
+ return isBeforeInTranslationUnitThan((SourceLocation)Loc);
+ }
+
/// Prints information about this FullSourceLoc to stderr. Useful for
/// debugging.
void dump() const { SourceLocation::dump(*SrcMgr); }
@@ -349,6 +364,19 @@ namespace llvm {
template <>
struct isPodLike<clang::FileID> { static const bool value = true; };
+ // Teach SmallPtrSet how to handle SourceLocation.
+ template<>
+ class PointerLikeTypeTraits<clang::SourceLocation> {
+ public:
+ static inline void *getAsVoidPointer(clang::SourceLocation L) {
+ return (void*)L.getRawEncoding();
+ }
+ static inline clang::SourceLocation getFromVoidPointer(void *P) {
+ return clang::SourceLocation::getFromRawEncoding((unsigned)(uintptr_t)P);
+ }
+ enum { NumLowBitsAvailable = 0 };
+ };
+
} // end namespace llvm
#endif
diff --git a/include/clang/Lex/MacroInfo.h b/include/clang/Lex/MacroInfo.h
index 90f95b7ace..717c3008ec 100644
--- a/include/clang/Lex/MacroInfo.h
+++ b/include/clang/Lex/MacroInfo.h
@@ -82,6 +82,9 @@ private:
/// AllowRedefinitionsWithoutWarning - True if this macro can be redefined
/// without emitting a warning.
bool IsAllowRedefinitionsWithoutWarning : 1;
+
+ /// \brief Must warn if the macro is unused at the end of translation unit.
+ bool IsWarnIfUnused : 1;
~MacroInfo() {
assert(ArgumentList == 0 && "Didn't call destroy before dtor!");
@@ -138,6 +141,11 @@ public:
IsAllowRedefinitionsWithoutWarning = Val;
}
+ /// \brief Set the value of the IsWarnIfUnused flag.
+ void setIsWarnIfUnused(bool val) {
+ IsWarnIfUnused = val;
+ }
+
/// setArgumentList - Set the specified list of identifiers as the argument
/// list for this macro.
void setArgumentList(IdentifierInfo* const *List, unsigned NumArgs,
@@ -201,6 +209,11 @@ public:
return IsAllowRedefinitionsWithoutWarning;
}
+ /// \brief Return true if we should emit a warning if the macro is unused.
+ bool isWarnIfUnused() const {
+ return IsWarnIfUnused;
+ }
+
/// getNumTokens - Return the number of tokens that this macro expands to.
///
unsigned getNumTokens() const {
diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h
index c2fcfe218e..1ced6a5800 100644
--- a/include/clang/Lex/Preprocessor.h
+++ b/include/clang/Lex/Preprocessor.h
@@ -25,6 +25,7 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Allocator.h"
@@ -196,6 +197,16 @@ class Preprocessor {
/// to the actual definition of the macro.
llvm::DenseMap<IdentifierInfo*, MacroInfo*> Macros;
+ /// \brief Macros that we want to warn because they are not used at the end
+ /// of the translation unit; we store just their SourceLocations instead
+ /// something like MacroInfo*. The benefit of this is that when we are
+ /// deserializing from PCH, we don't need to deserialize identifier & macros
+ /// just so that we can report that they are unused, we just warn using
+ /// the SourceLocations of this set (that will be filled by the ASTReader).
+ /// We are using SmallPtrSet instead of a vector for faster removal.
+ typedef llvm::SmallPtrSet<SourceLocation, 32> WarnUnusedMacroLocsTy;
+ WarnUnusedMacroLocsTy WarnUnusedMacroLocs;
+
/// MacroArgCache - This is a "freelist" of MacroArg objects that can be
/// reused for quick allocation.
MacroArgs *MacroArgCache;
@@ -1013,6 +1024,10 @@ public:
// Return true and store the first token only if any CommentHandler
// has inserted some tokens and getCommentRetentionState() is false.
bool HandleComment(Token &Token, SourceRange Comment);
+
+ /// \brief A macro is used, update information about macros that need unused
+ /// warnings.
+ void markMacroAsUsed(MacroInfo *MI);
};
/// \brief Abstract base class that describes a handler that will receive