9797import com .sun .source .util .JavacTask ;
9898import com .sun .source .util .TaskEvent ;
9999import com .sun .source .util .TaskListener ;
100+ import com .sun .source .util .TreePath ;
100101import com .sun .tools .javac .api .JavacTool ;
101102import com .sun .tools .javac .api .MultiTaskListener ;
102103import com .sun .tools .javac .code .Symbol .PackageSymbol ;
103104import com .sun .tools .javac .comp .CompileStates .CompileState ;
104105import com .sun .tools .javac .file .JavacFileManager ;
106+ import com .sun .tools .javac .main .Arguments ;
105107import com .sun .tools .javac .main .Option ;
106108import com .sun .tools .javac .main .Option .OptionKind ;
107109import com .sun .tools .javac .parser .JavadocTokenizer ;
122124import com .sun .tools .javac .util .Names ;
123125import com .sun .tools .javac .util .Options ;
124126
127+ import jdk .javadoc .internal .doclint .DocLint ;
128+
125129/**
126130 * Allows to create and resolve DOM ASTs using Javac
127131 * @implNote Cannot move to another package because parent class is package visible only
@@ -611,6 +615,71 @@ private Map<org.eclipse.jdt.internal.compiler.env.ICompilationUnit, CompilationU
611615 final UnusedProblemFactory unusedProblemFactory = new UnusedProblemFactory (new DefaultProblemFactory (), compilerOptions );
612616 var problemConverter = new JavacProblemConverter (compilerOptions , context );
613617 DiagnosticListener <JavaFileObject > diagnosticListener = new ForwardDiagnosticsAsDOMProblems (filesToUnits , problemConverter );
618+ // must be 1st thing added to context
619+ context .put (DiagnosticListener .class , diagnosticListener );
620+ Map <JavaFileObject , File > fileObjectsToJars = new HashMap <>();
621+ context .put (FILE_OBJECTS_TO_JAR_KEY , fileObjectsToJars );
622+ boolean docEnabled = JavaCore .ENABLED .equals (compilerOptions .get (JavaCore .COMPILER_DOC_COMMENT_SUPPORT ));
623+ // ignore module is a workaround for cases when we read a module-info.java from a library.
624+ // Such units cause a failure later because their name is lost in ASTParser and Javac cannot treat them as modules
625+ boolean ignoreModule = !Arrays .stream (sourceUnits ).allMatch (u -> new String (u .getFileName ()).endsWith ("java" ));
626+ JavacUtils .configureJavacContext (context , compilerOptions , javaProject , JavacUtils .isTest (javaProject , sourceUnits ), ignoreModule );
627+ Options javacOptions = Options .instance (context );
628+ javacOptions .put ("allowStringFolding" , Boolean .FALSE .toString ()); // we need to keep strings as authored
629+ if (focalPoint >= 0 ) {
630+ // Skip doclint by default, will be re-enabled in the TaskListener if focalPoint is in Javadoc
631+ javacOptions .remove (Option .XDOCLINT .primaryName );
632+ javacOptions .remove (Option .XDOCLINT_CUSTOM .primaryName );
633+ // minimal linting, but "raw" still seems required
634+ javacOptions .put (Option .XLINT_CUSTOM , "raw" );
635+ } else if ((flags & ICompilationUnit .FORCE_PROBLEM_DETECTION ) == 0 ) {
636+ // minimal linting, but "raw" still seems required
637+ javacOptions .put (Option .XLINT_CUSTOM , "raw" );
638+ // set minimal custom DocLint support to get DCComment bindings resolved
639+ javacOptions .put (Option .XDOCLINT_CUSTOM , "reference" );
640+ }
641+ javacOptions .put (Option .PROC , ProcessorConfig .isAnnotationProcessingEnabled (javaProject ) ? "only" : "none" );
642+ Optional .ofNullable (Platform .getProduct ())
643+ .map (IProduct ::getApplication )
644+ // if application is not a test runner (so we don't have regressions with JDT test suite because of too many problems
645+ .or (() -> Optional .ofNullable (System .getProperty ("eclipse.application" )))
646+ .filter (name -> !name .contains ("test" ) && !name .contains ("junit" ))
647+ // continue as far as possible to get extra warnings about unused
648+ .ifPresent (_ ->javacOptions .put ("should-stop.ifError" , CompileState .GENERATE .toString ()));
649+ JavacFileManager fileManager = (JavacFileManager )context .get (JavaFileManager .class );
650+ if (javaProject == null && extraClasspath != null ) {
651+ try {
652+ fileManager .setLocation (StandardLocation .CLASS_PATH , extraClasspath .stream ()
653+ .map (Classpath ::getPath )
654+ .map (File ::new )
655+ .toList ());
656+ } catch (IOException ex ) {
657+ ILog .get ().error (ex .getMessage (), ex );
658+ }
659+ }
660+ List <JavaFileObject > fileObjects = new ArrayList <>(); // we need an ordered list of them
661+ for (org .eclipse .jdt .internal .compiler .env .ICompilationUnit sourceUnit : sourceUnits ) {
662+ char [] sourceUnitFileName = sourceUnit .getFileName ();
663+ JavaFileObject fileObject = cuToFileObject (javaProject , sourceUnitFileName , sourceUnit , fileManager , fileObjectsToJars );
664+ fileManager .cache (fileObject , CharBuffer .wrap (sourceUnit .getContents ()));
665+ AST ast = createAST (compilerOptions , apiLevel , context , flags );
666+ CompilationUnit res = ast .newCompilationUnit ();
667+ result .put (sourceUnit , res );
668+ filesToUnits .put (fileObject , res );
669+ fileObjects .add (fileObject );
670+ }
671+
672+ // some options needs to be passed to getTask() to be properly handled
673+ // (just having them set in Options is sometimes not enough). So we
674+ // turn them back into CLI arguments to pass them.
675+ List <String > options = new ArrayList <>(toCLIOptions (javacOptions ));
676+ if (!configureAPTIfNecessary (fileManager )) {
677+ options .add ("-proc:none" );
678+ }
679+
680+ options = replaceSafeSystemOption (options );
681+ addSourcesWithMultipleTopLevelClasses (sourceUnits , fileObjects , javaProject , fileManager );
682+ JavacTask task = ((JavacTool )compiler ).getTask (null , fileManager , null /* already added to context */ , options , List .of () /* already set */ , fileObjects , context );
614683 MultiTaskListener .instance (context ).add (new TaskListener () {
615684 @ Override
616685 public void finished (TaskEvent e ) {
@@ -623,6 +692,18 @@ public void finished(TaskEvent e) {
623692 trimNonFocusedContent (u , focalPoint );
624693 }
625694
695+ var doclintOpts = Arguments .instance (context ).getDocLintOpts ();
696+ if (e .getKind () == TaskEvent .Kind .ANALYZE &&
697+ focalPoint >= 0 &&
698+ doclintOpts == null &&
699+ e .getCompilationUnit () instanceof JCCompilationUnit u &&
700+ isInJavadoc (u , focalPoint )) {
701+ // resolve doc comment bindings
702+ DocLint doclint = (DocLint )DocLint .newDocLint ();
703+ doclint .init (task , doclintOpts .toArray (new String [doclintOpts .size ()]));
704+ doclint .scan (TreePath .getPath (u , u ));
705+ }
706+
626707 if (e .getKind () == TaskEvent .Kind .ANALYZE && Options .instance (context ).get (Option .XLINT_CUSTOM ).contains ("all" )) {
627708 final JavaFileObject file = e .getSourceFile ();
628709 final CompilationUnit dom = filesToUnits .get (file );
@@ -694,7 +775,7 @@ public Void visitClass(ClassTree node, Void p) {
694775 }
695776 }
696777
697- private void trimNonFocusedContent (JCCompilationUnit compilationUnit , int focalPoint ) {
778+ private static void trimNonFocusedContent (JCCompilationUnit compilationUnit , int focalPoint ) {
698779 if (focalPoint < 0 ) {
699780 return ;
700781 }
@@ -726,66 +807,30 @@ public void scan(JCTree tree) {
726807 }
727808 });
728809 }
729- });
730- // must be 1st thing added to context
731- context .put (DiagnosticListener .class , diagnosticListener );
732- Map <JavaFileObject , File > fileObjectsToJars = new HashMap <>();
733- context .put (FILE_OBJECTS_TO_JAR_KEY , fileObjectsToJars );
734- boolean docEnabled = JavaCore .ENABLED .equals (compilerOptions .get (JavaCore .COMPILER_DOC_COMMENT_SUPPORT ));
735- // ignore module is a workaround for cases when we read a module-info.java from a library.
736- // Such units cause a failure later because their name is lost in ASTParser and Javac cannot treat them as modules
737- boolean ignoreModule = !Arrays .stream (sourceUnits ).allMatch (u -> new String (u .getFileName ()).endsWith ("java" ));
738- JavacUtils .configureJavacContext (context , compilerOptions , javaProject , JavacUtils .isTest (javaProject , sourceUnits ), ignoreModule );
739- Options javacOptions = Options .instance (context );
740- javacOptions .put ("allowStringFolding" , Boolean .FALSE .toString ()); // we need to keep strings as authored
741- if (focalPoint >= 0 || (flags & ICompilationUnit .FORCE_PROBLEM_DETECTION ) == 0 ) {
742- // most likely no need for more linting
743- // resolveBindings still seems requested for tests
744- javacOptions .put (Option .XLINT_CUSTOM , "raw" );
745- javacOptions .put (Option .XDOCLINT_CUSTOM , "none" );
746- }
747- javacOptions .put (Option .PROC , ProcessorConfig .isAnnotationProcessingEnabled (javaProject ) ? "only" : "none" );
748- Optional .ofNullable (Platform .getProduct ())
749- .map (IProduct ::getApplication )
750- // if application is not a test runner (so we don't have regressions with JDT test suite because of too many problems
751- .or (() -> Optional .ofNullable (System .getProperty ("eclipse.application" )))
752- .filter (name -> !name .contains ("test" ) && !name .contains ("junit" ))
753- // continue as far as possible to get extra warnings about unused
754- .ifPresent (_ ->javacOptions .put ("should-stop.ifError" , CompileState .GENERATE .toString ()));
755- JavacFileManager fileManager = (JavacFileManager )context .get (JavaFileManager .class );
756- if (javaProject == null && extraClasspath != null ) {
757- try {
758- fileManager .setLocation (StandardLocation .CLASS_PATH , extraClasspath .stream ()
759- .map (Classpath ::getPath )
760- .map (File ::new )
761- .toList ());
762- } catch (IOException ex ) {
763- ILog .get ().error (ex .getMessage (), ex );
764- }
765- }
766- List <JavaFileObject > fileObjects = new ArrayList <>(); // we need an ordered list of them
767- for (org .eclipse .jdt .internal .compiler .env .ICompilationUnit sourceUnit : sourceUnits ) {
768- char [] sourceUnitFileName = sourceUnit .getFileName ();
769- JavaFileObject fileObject = cuToFileObject (javaProject , sourceUnitFileName , sourceUnit , fileManager , fileObjectsToJars );
770- fileManager .cache (fileObject , CharBuffer .wrap (sourceUnit .getContents ()));
771- AST ast = createAST (compilerOptions , apiLevel , context , flags );
772- CompilationUnit res = ast .newCompilationUnit ();
773- result .put (sourceUnit , res );
774- filesToUnits .put (fileObject , res );
775- fileObjects .add (fileObject );
776- }
777810
778- // some options needs to be passed to getTask() to be properly handled
779- // (just having them set in Options is sometimes not enough). So we
780- // turn them back into CLI arguments to pass them.
781- List <String > options = new ArrayList <>(toCLIOptions (javacOptions ));
782- if (!configureAPTIfNecessary (fileManager )) {
783- options .add ("-proc:none" );
784- }
785-
786- options = replaceSafeSystemOption (options );
787- addSourcesWithMultipleTopLevelClasses (sourceUnits , fileObjects , javaProject , fileManager );
788- JavacTask task = ((JavacTool )compiler ).getTask (null , fileManager , null /* already added to context */ , options , List .of () /* already set */ , fileObjects , context );
811+ private static boolean isInJavadoc (JCCompilationUnit u , int focalPoint ) {
812+ boolean [] res = new boolean [] { false };
813+ u .accept (new TreeScanner () {
814+ @ Override
815+ public void scan (JCTree tree ) {
816+ if (res [0 ]) {
817+ return ;
818+ }
819+ var comment = u .docComments .getComment (tree );
820+ if (comment != null &&
821+ comment .getPos ().getStartPosition () < focalPoint &&
822+ focalPoint < comment .getPos ().getEndPosition (u .endPositions ) &&
823+ (comment .getStyle () == CommentStyle .JAVADOC_BLOCK ||
824+ comment .getStyle () == CommentStyle .JAVADOC_LINE )) {
825+ res [0 ] = true ;
826+ return ;
827+ }
828+ super .scan (tree );
829+ }
830+ });
831+ return res [0 ];
832+ }
833+ });
789834 {
790835 // don't know yet a better way to ensure those necessary flags get configured
791836 var javac = com .sun .tools .javac .main .JavaCompiler .instance (context );
0 commit comments