[cfe-commits] r59502 - in /cfe/trunk: Driver/ include/clang/Analysis/ include/clang/Analysis/PathSensitive/ include/clang/Basic/ include/clang/Driver/ lib/Analysis/ lib/Basic/ lib/CodeGen/ lib/Driver/ lib/Lex/ lib/Parse/ lib/Sema/

Douglas Gregor dgregor at apple.com
Tue Nov 18 08:13:44 PST 2008


On Nov 18, 2008, at 2:04 AM, Chris Lattner wrote:

> Author: lattner
> Date: Tue Nov 18 01:04:44 2008
> New Revision: 59502
>
> URL: http://llvm.org/viewvc/llvm-project?rev=59502&view=rev
> Log:
> This reworks some of the Diagnostic interfaces a bit to change how  
> diagnostics
> are formed.  In particular, a diagnostic with all its strings and  
> ranges is now
> packaged up and sent to DiagnosticClients as a DiagnosticInfo  
> instead of as a
> ton of random stuff.  This has the benefit of simplifying the  
> interface, making
> it more extensible, and allowing us to do more checking for things  
> like access
> past the end of the various arrays passed in.

Cool, thanks for tackling this.

> We will soon be able to just do:
>
>  Diag(BuiltinLoc, diag::err_overload_no_match)
>      << typeNames << SourceRange(BuiltinLoc, RParenLoc));
>
> This scales better to support arbitrary types being passed in (not  
> just
> strings) in a type-safe way.  Go operator overloading?!

Looks good.

> +class DiagnosticInfo {
> +  Diagnostic *DiagObj;
> +  FullSourceLoc Loc;
> +  unsigned DiagID;
> +  void operator=(const DiagnosticInfo&); // DO NOT IMPLEMENT
> +public:
> +  DiagnosticInfo(Diagnostic *diagObj, FullSourceLoc loc, unsigned  
> diagID) :
> +    DiagObj(diagObj), Loc(loc), DiagID(diagID) {
> +    assert(DiagObj->NumDiagArgs == -1 &&
> +           "Multiple diagnostics in flight at once!");
> +    DiagObj->NumDiagArgs = DiagObj->NumDiagRanges = 0;
> +  }
> +
> +  /// Copy constructor.  When copied, this "takes" the diagnostic  
> info from the
> +  /// input and neuters it.
> +  DiagnosticInfo(DiagnosticInfo &D) {
> +    DiagObj = D.DiagObj;
> +    Loc = D.Loc;
> +    DiagID = D.DiagID;
> +    D.DiagObj = 0;
> +  }
> +
> +  /// Destructor - The dtor emits the diagnostic.
> +  ~DiagnosticInfo() {
> +    // If DiagObj is null, then its soul was stolen by the copy ctor.
> +    if (!DiagObj) return;
> +
> +    DiagObj->ProcessDiag(*this);
> +
> +    // This diagnostic is no longer in flight.
> +    DiagObj->NumDiagArgs = -1;
> +  }
> +
> +  const Diagnostic *getDiags() const { return DiagObj; }
> +  unsigned getID() const { return DiagID; }
> +  const FullSourceLoc &getLocation() const { return Loc; }
>

Is there an easy way to determine whether this diagnostic has been  
suppressed, such as a method

	bool isSuppressed() const;

?

If so, then the operator<<'s for DiagnosticInfo could first check  
isSuppressed(), and abort early if the diagnostic is suppressed. That  
way, we never pay for formatting strings (e.g., for a type name or  
expression) or copying them (see my comment below).

> +  DiagnosticInfo &operator<<(const std::string &S) {
> +    assert((unsigned)DiagObj->NumDiagArgs <
> +           sizeof(DiagObj->DiagArguments)/sizeof(DiagObj- 
> >DiagArguments[0]) &&
> +           "Too many arguments to diagnostic!");
> +    DiagObj->DiagArguments[DiagObj->NumDiagArgs++] = &S;
> +    return *this;
> +  }

Oh, I really don't like the &S here. I know that in the use cases  
you're thinking of, the whole diagnostic will happen on one line:

	Diag(location, id) << foo.getAsString() << ... ;

However, it seems plausible to me that we might also want to grab a  
name for the result of Diag() and then add more information to it  
afterward:

	DiagnosticInfo D = Diag(location, id) << foo.getAsString << .. ;
	// add more stuff to D
	// on destruction of D, we crash (probably)

The case I'm thinking about most is for reporting the results of an  
overload resolution failure, where we currently output several related  
diagnostics. The first says what the problem is:

overload-call.cpp:184:3: error: call to 'quals_rank3' is ambiguous;  
candidates are:
   quals_rank3(p); // expected-error {{call to 'quals_rank3' is  
ambiguous; candidates are:}}
   ^~~~~~~~~~~

then we have some follow-on "notes"  that show the candidates:

overload-call.cpp:168:6: note: candidate function
void quals_rank3(int const *); // expected-note{{candidate function}}
      ^
overload-call.cpp:169:6: note: candidate function
void quals_rank3(int volatile *); // expected-note{{candidate function}}
      ^

It seems wrong (to me!) to have these be separate errors; rather,  
these notes should be formatted as part of the original message,  
because it's all (conceptually) one error, and an IDE client would  
want to treat it as one error (perhaps with some interactive  
exploration of the candidate functions). There's a lot of info we  
could put into that DiagnosticInfo object, but it's tougher if we  
can't name it :)

There are other reasons not to like the &S. For example, if we're  
outputting a QualType named "Ty" through a diagnostic, e.g.,

	Diag(Lok, ID) << Ty;

we're going to need to create a temporary std::string to store the  
formatted type name, but where do we store that temporary so that  
DiagnosticInfo can hold on to it?

Besides, if we have isSuppressed(), we'll only be doing string copying  
and formatting when the diagnostic is actually going to be displayed,  
so we're not as worried about performance once we're emitting the  
diagnostic.

> +  DiagnosticInfo &operator<<(const SourceRange &R) {
> +    assert((unsigned)DiagObj->NumDiagArgs <
> +           sizeof(DiagObj->DiagRanges)/sizeof(DiagObj- 
> >DiagRanges[0]) &&
> +           "Too many arguments to diagnostic!");
> +    DiagObj->DiagRanges[DiagObj->NumDiagRanges++] = &R;
> +    return *this;
> +  }
> +
> };

These operator<<'s should be free functions, because we will certainly  
have other operator<<'s for DiagnosticInfo elsewhere in the compiler  
that must be free functions, e.g.,

	DiagnosticInfo& operator<<(DiagnosticInfo& DI, QualType Ty); // in  
clang/AST/Type.h

and we don't want non-member and member operator<<'s overloaded  
because the available conversions on the first argument differ, and  
that causes overloading weirdness.

	- Doug



More information about the cfe-commits mailing list