aaron.ballman accepted this revision.
aaron.ballman added a comment.
This revision is now accepted and ready to land.

Clang parts LGTM, thank you for this!



================
Comment at: clang/test/Sema/attr-error.c:31-32
+
+__attribute__((error("foo"))) int bad5(void);   // expected-error {{'error' 
and 'warning' attributes are not compatible}}
+__attribute__((warning("foo"))) int bad5(void); // expected-note {{conflicting 
attribute is here}}
+
----------------
nickdesaulniers wrote:
> aaron.ballman wrote:
> > nickdesaulniers wrote:
> > > aaron.ballman wrote:
> > > > I think the diagnostic order is backwards here. The first declaration 
> > > > is where I'd expect the note and the second declaration is where I'd 
> > > > expect the error. (The idea is: the first declaration adds an attribute 
> > > > to the decl, so the redeclaration is what introduces the conflict and 
> > > > so that's where the error should live.) As an example: 
> > > > https://godbolt.org/z/bjGTWxYvh
> > > I'm not sure how to reconcile this. Looking at `bad4` above (different 
> > > case than `bad5`), the diagnostic we get is:
> > > ```
> > > /tmp/y.c:1:30: error: 'warning' and 'error' attributes are not compatible
> > > __attribute__((error("foo"), warning("foo")))
> > >                              ^
> > > /tmp/y.c:1:16: note: conflicting attribute is here
> > > __attribute__((error("foo"), warning("foo")))
> > >                ^
> > > 1 error generated.
> > > ```
> > > which looks correct to me. If I flip the order these are diagnosed in, 
> > > that fixes `bad5` but if I flip around the ordering:
> > > ```
> > > --- a/clang/lib/Sema/SemaDeclAttr.cpp
> > > +++ b/clang/lib/Sema/SemaDeclAttr.cpp
> > > -      Diag(CI.getLoc(), diag::err_attributes_are_not_compatible) << CI 
> > > << EA;
> > > -      Diag(EA->getLocation(), diag::note_conflicting_attribute);
> > > +      Diag(EA->getLocation(), diag::err_attributes_are_not_compatible)
> > > +          << CI << EA;
> > > +      Diag(CI.getLoc(), diag::note_conflicting_attribute);
> > > ```
> > > Then `bad4` doesn't look quite correct.
> > > ```
> > > /tmp/y.c:1:16: error: 'warning' and 'error' attributes are not compatible
> > > __attribute__((error("foo"), warning("foo")))
> > >                ^
> > > /tmp/y.c:1:30: note: conflicting attribute is here
> > > __attribute__((error("foo"), warning("foo")))
> > >                              ^
> > > ```
> > > Perhaps in the callers of `handleErrorAttr` I'm confusing which `Decl` is 
> > > the "new" vs the "old?"
> > > which looks correct to me.
> > 
> > That also looks correct to me; the second attribute is the diagnostic and 
> > the first attribute is the note.
> > 
> > > Perhaps in the callers of handleErrorAttr I'm confusing which Decl is the 
> > > "new" vs the "old?"
> > 
> > Huh, this is rather strange. The logic you're using is basically the same 
> > as `checkAttrMutualExclusion()`, just with extra work to figure out which 
> > kind of attribute you're dealing with. I'm not seeing what's causing the 
> > order to be different when I reason my way through the code. Perhaps in the 
> > `bad4()` case, are we somehow processing the attributes in reverse order?
> > 
> > FWIW, at the end of the day, we can live with the diagnostic in either 
> > situation, even if they're a bit strange. I think the merge behavior (two 
> > different decls) is more important to get right because that's going to be 
> > the far more common scenario to see the diagnostic in. If it turns out that 
> > it's a systemic issue (or just gnarly to figure out), it won't block this 
> > review. We can always improve the behavior in a follow-up.
> I suspect that `DiagnoseMutualExclusions` (via tablegen) would be able to 
> catch this, had we distinct `Attr`s for warning and error; example tablegen 
> rule: `def : MutualExclusions<[Hot, Cold]>;`.
I suspect you're correct. Perhaps the logic in ClangAttrEmitter.cpp is already 
accounting for some oddity here that we're not. The way the generated mutual 
exclusions look are:
```
  if (const auto *Second = dyn_cast<AlwaysDestroyAttr>(A)) {
    if (const auto *First = D->getAttr<NoDestroyAttr>()) {
      S.Diag(First->getLocation(), diag::err_attributes_are_not_compatible) << 
First << Second;
      S.Diag(Second->getLocation(), diag::note_conflicting_attribute);
      return false;
    }
    return true;
  }
```
Compared with your code, this is different. In your code, `CI` is analogous to 
`Second` above, and `EA` is analogous to `First`. You diagnose at the location 
of `First` but pass in `<< Second (CI) << First (EA)` as the arguments.
```
ErrorAttr *Sema::mergeErrorAttr(Decl *D, const AttributeCommonInfo &CI,
                                StringRef NewUserDiagnostic) {
  if (const auto *EA = D->getAttr<ErrorAttr>()) {
    std::string NewAttr = CI.getNormalizedFullName();
    assert((NewAttr == "error" || NewAttr == "warning") &&
           "unexpected normalized full name");
    bool Match = (EA->isError() && NewAttr == "error") ||
                 (EA->isWarning() && NewAttr == "warning");
    if (!Match) {
      Diag(EA->getLocation(), diag::err_attributes_are_not_compatible)
          << CI << EA;
      Diag(CI.getLoc(), diag::note_conflicting_attribute);
      return nullptr;
    }
    if (EA->getUserDiagnostic() != NewUserDiagnostic) {
      Diag(CI.getLoc(), diag::warn_duplicate_attribute) << EA;
      Diag(EA->getLoc(), diag::note_previous_attribute);
    }
    D->dropAttr<ErrorAttr>();
  }
  return ::new (Context) ErrorAttr(Context, CI, NewUserDiagnostic);
}
```
Regardless, I'm content with the behavior of your tests, so I think we can move 
on.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D106030/new/

https://reviews.llvm.org/D106030

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to