llvm/llvm-project

Source range of deleted or defaulted function outside class body is inaccurate

Open

#64 805 ouverte le 18 août 2023

Voir sur GitHub
 (22 commentaires) (0 réactions) (0 assignés)C++ (10 782 forks)batch import
clang:frontendgood first issue

Métriques du dépôt

Stars
 (26 378 stars)
Métriques de merge PR
 (Merge moyen 1j 2h) (1 000 PRs mergées en 30 j)

Description

Problem

For this translation unit:

void delfunc() = delete;

the source range of delfunc should cover the entire text to the final e in delete, but when using Clang 16.0.0 (and also a recent-ish trunk build from source), it does not:

$ clang -Xclang -ast-dump -fsyntax-only tmp.cc
[...]
`-FunctionDecl 0x5613b9a09b28 <tmp.cc:1:1, col:14> col:6 delfunc 'void ()' delete implicit-inline
                                               ^^ expecting 23 here

The same thing happens for methods defined outside their class body:

$ cat tmp3.cc
struct S {
  inline S();
};

inline S::S() = default;

$ clang -Xclang -ast-dump -fsyntax-only tmp3.cc
[...]
`-CXXConstructorDecl 0x557217b67278 parent 0x557217b66af0 prev 0x557217b66d28 <line:5:1, col:13> col:11 used S 'void ()' inline default
  `-CompoundStmt 0x557217b67368 <col:13>

where col:13 should be col:23.

Suspected origin

Commit 5f4d76efd365c (2015-03-23) addressed a similar issue for declarations in the class body in ParseCXXInlineMethods.cpp, but did not change the handling of those outside class bodies in Parser.cpp.

Rough sketch of a fix

The following diff handles the simple testcases above. However, it does not handle templates, and I don't know enough about the parser to fix that myself. I also have not done any testing with real code. So, this diff is merely evidence that something in this vicinity probably needs to change rather than a serious proposal for exactly how to change it.

--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1354,6 +1359,10 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
   ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope |
                                  Scope::CompoundStmtScope);
 
+  // If this is set, we need to adjust the end location to account for
+  // the '= delete' or '= default'.
+  SourceLocation KWEndLoc_toUpdate;
+
   // Parse function body eagerly if it is either '= delete;' or '= default;' as
   // ActOnStartOfFunctionDef needs to know whether the function is deleted.
   Sema::FnBodyKind BodyKind = Sema::FnBodyKind::Other;
@@ -1361,18 +1370,21 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
   if (TryConsumeToken(tok::equal)) {
     assert(getLangOpts().CPlusPlus && "Only C++ function definitions have '='");
 
+    SourceLocation KWEndLoc = Tok.getEndLoc().getLocWithOffset(-1);
     if (TryConsumeToken(tok::kw_delete, KWLoc)) {
       Diag(KWLoc, getLangOpts().CPlusPlus11
                       ? diag::warn_cxx98_compat_defaulted_deleted_function
                       : diag::ext_defaulted_deleted_function)
           << 1 /* deleted */;
       BodyKind = Sema::FnBodyKind::Delete;
+      KWEndLoc_toUpdate = KWEndLoc;
     } else if (TryConsumeToken(tok::kw_default, KWLoc)) {
       Diag(KWLoc, getLangOpts().CPlusPlus11
                       ? diag::warn_cxx98_compat_defaulted_deleted_function
                       : diag::ext_defaulted_deleted_function)
           << 0 /* defaulted */;
       BodyKind = Sema::FnBodyKind::Default;
+      KWEndLoc_toUpdate = KWEndLoc;
     } else {
       llvm_unreachable("function definition after = not 'delete' or 'default'");
     }
@@ -1398,6 +1410,13 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
                                                   : MultiTemplateParamsArg(),
                                               &SkipBody, BodyKind);
 
+  if (KWEndLoc_toUpdate.isValid()) {
+    // TODO: This does not handle templates.
+    if (auto *DeclAsFunction = dyn_cast<FunctionDecl>(Res)) {
+      DeclAsFunction->setRangeEnd(KWEndLoc_toUpdate);
+    }
+  }
+
   if (SkipBody.ShouldSkip) {
     // Do NOT enter SkipFunctionBody if we already consumed the tokens.
     if (BodyKind == Sema::FnBodyKind::Other)

Guide contributeur