This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Full support for #import insertions
ClosedPublic

Authored by dgoldman on Dec 6 2022, 1:39 PM.

Details

Summary

These are still disabled by default, but will work in ObjC code if you
enable the -import-insertions flag.

This requires ASTSignals to be available; before ASTSignals are
available, we will always use #include. Once they are available, the
behavior varies as follows:

  • For source files, use #import if the ObjC language flag is enabled
  • For header files:
    • If the ObjC language flag is disabled, use #include
    • If the header file contains any #imports, use #import
    • If the header file references any ObjC decls, use #import
    • Otherwise, use #include

Diff Detail

Event Timeline

dgoldman created this revision.Dec 6 2022, 1:39 PM
Herald added a project: Restricted Project. · View Herald TranscriptDec 6 2022, 1:39 PM
dgoldman requested review of this revision.Dec 6 2022, 1:39 PM
dgoldman updated this revision to Diff 481022.Dec 7 2022, 12:46 PM

Add tests

kadircet added inline comments.Dec 13 2022, 6:50 AM
clang-tools-extra/clangd/ASTSignals.cpp
29

can we rewrite this as:

bool preferImports(const ParsedAST &AST) {
  // If we don't have any objc-ness, always prefer #include
  if (!AST.getLangOpts().ObjC)
     return false;
  // If this is not a header file and has objc set as language, prefer #import
  if (!isHeaderFile(...))
     return true;
  // Headers lack proper compile flags most of the time, so we might treat a header as objc accidentally.
  // Perform some extra checks to make sure this works. 

  // Any file with a #import, should keep #import-ing.
  for (auto &Inc : AST.getIncludeStructure().MainFileIncludes) {
    if (Inc.Directive == tok::pp_import)
       return true;
  }
  
  // Any file declaring an objc struct should also #import.
  // No need to look over the references, as the file doesn't have any #imports, it must be declaring interesting Objc-like decls.
  for(Decl *D: AST.getLocalTopLevelDecls()) {
        if (isa<...InterestingDeclTypes...>(D)
           return true;
   });
  return false;
}

It's fine to run findExplicitReferences for an extra time here, as we do it only when this is an ObjC header with no #imports

clang-tools-extra/clangd/ASTSignals.h
33

can we rather say Preferred PP-directive to use for inclusions by the file.

clang-tools-extra/clangd/ParsedAST.cpp
571

we already have access to Clang->getLangOpts() here. we also have the FileName available so we can check for header-ness as well. we also have preamble includes to scan for #import directives (well, we might be building an AST without a preamble sometimes but it's fine if IncludeFixer can't suggest #imports in those cases).

so can we detect import-ness based on these here? we're only lacking information about whether the file declares any objc symbols, e.g. we might fail to suggest #import directives in a objc file without any #import statements, but I'd say it's fine considering all the extra complexity needed to propagate it from previous run. (if this turns out to be an important UX issue we can consider plumbing this information directly, so please leave a FIXME).

clang-tools-extra/clangd/unittests/ASTSignalsTests.cpp
91

can you put this test first with a comment like objc lang option is not enough for headers?

97

this only works because we actually implicitly #import contents of HeaderTU.HeaderCode in TestTU.

can you instead clear up the HeaderCode and define the interface directly in the header file?

clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
2747–2750

can you use this Opts (without Signals) in the previous test as well to make sure we're #includeing the symbol in a non-import context even if the option is set?

dgoldman updated this revision to Diff 482950.Dec 14 2022, 11:30 AM
dgoldman marked 5 inline comments as done.

Fixes for review

dgoldman updated this revision to Diff 483205.Dec 15 2022, 8:43 AM

Don't pass ASTSignals through ParsedAST

dgoldman marked an inline comment as done.Dec 15 2022, 8:46 AM
kadircet added inline comments.Jan 2 2023, 2:30 AM
clang-tools-extra/clangd/ASTSignals.h
39

could you rather move this to AST.h, with some comments explaining what it does

41

what about accepting an llvm::ArrayRef<Inclusion> MainFileIncludes instead of the full IncludeStructure, as the former can be constructed a lot more easily.

clang-tools-extra/clangd/CodeComplete.cpp
405

nit: I'd actually inline this into use-side to not confuse the reader unnecessarily about whether this is marshalling/conversion

clang-tools-extra/clangd/IncludeFixer.cpp
321

again, i'd inline the marshalling

clang-tools-extra/clangd/ParsedAST.cpp
567

this is a somewhat heavy struct to copy (contains absolute paths to all the includes in the preamble). can you use a pointer here?

clang-tools-extra/clangd/ParsedAST.h
88–89

can you rather introduce a new overload that returns ArrayRef<const Decl*>

clang-tools-extra/clangd/unittests/ASTSignalsTests.cpp
87

since we have a dedicated interface now, could you please test it instead (same for uses below)?

98

no need for the #include here

dgoldman updated this revision to Diff 486111.Jan 3 2023, 4:01 PM
dgoldman marked 7 inline comments as done.

Minor fixes for review, moving stuff around

dgoldman added inline comments.Jan 3 2023, 8:11 PM
clang-tools-extra/clangd/ParsedAST.h
88–89

Did you mean an overload with ArrayRef<const Decl *> getLocalTopLevelDecls() const;? ASTSignals gets a const ParsedAST &, unless you want me to const cast instead?

kadircet accepted this revision.Jan 3 2023, 11:42 PM
kadircet added inline comments.
clang-tools-extra/clangd/AST.h
123

Maybe something more concise like:

Infer the include directive to use for the given \p FileName. It aims for #import for ObjC files and #include for the rest.

For source files we use LangOpts directly to infer ObjC-ness.
For header files we also check for symbols declared by the file and existing include directives, as the language can be set to objc as a fallback in the absence of compile flags.

To make sure we're first talking about "what" it's trying to achieve and then talk about "how". That way future travelers can verify their changes to heuristics to see if they're breaking the semantics.

clang-tools-extra/clangd/ParsedAST.h
88–89

yes I meant exactly that signature.

clang-tools-extra/clangd/unittests/ASTTests.cpp
622

nit: I'd make this a lambda in the test below.

This revision is now accepted and ready to land.Jan 3 2023, 11:42 PM
dgoldman updated this revision to Diff 486291.Jan 4 2023, 7:39 AM
dgoldman marked 4 inline comments as done.

Update comment + test

kadircet requested changes to this revision.Jan 5 2023, 7:45 AM
kadircet added inline comments.
clang-tools-extra/clangd/ParsedAST.cpp
573

as discussed offline can you guard this with the import-insertion flag as well? we should pass it through ParseOptions.

This revision now requires changes to proceed.Jan 5 2023, 7:45 AM
dgoldman updated this revision to Diff 486706.Jan 5 2023, 4:07 PM

Respect ImportInsertions flag in IncludeFixer

dgoldman marked an inline comment as done.Jan 5 2023, 4:07 PM
dgoldman added inline comments.
clang-tools-extra/clangd/ParsedAST.cpp
573

Done, LMK if I did that properly.

kadircet accepted this revision.Jan 9 2023, 1:02 AM

thanks, lgtm!

This revision is now accepted and ready to land.Jan 9 2023, 1:02 AM
This revision was landed with ongoing or failed builds.Jan 9 2023, 7:04 AM
This revision was automatically updated to reflect the committed changes.
dgoldman marked an inline comment as done.